Cannot set Keycloak user attribute in EventListenerProvider

83 Views Asked by At

When using "onEvent" method in my custom EventListenerProvider, I need to set attribute "isAfterFirstLogin" to "true", but only get exception: java.lang.IllegalStateException: Cannot access delegate without a transaction

I was trying to use KeycloakTransactionManager transactionManager = session.getTransactionManager(); transactionManager.begin(); transactionManager.commit(); and KeycloakModelUtils.runJobInTransaction

but didn't succeed (the same exception). Fetching the attributes map, and manually putting the attribute won't help, as the change won't be persisted and will be reset after user's session is closed.

Is there any workaround for the issue or any other way how it can be handled?

Keycloak version: 23.0.5

2

There are 2 best solutions below

0
csbrogi On

For me the followin works to set an Attribute

public MyEventListenerProvider(KeycloakSession keycloakSession) {
        this.keycloakSession = keycloakSession;
    }


    @Override
    public void onEvent(Event event) {
        log.info(String.format("KeycloakUserEvent:%s:%s", event.getType(), event.getClientId()));
        String userId = event.getUserId();
        EventType eventType = event.getType();
        if (userId != null) {
            RealmModel realm = keycloakSession.realms().getRealm(event.getRealmId());
            UserModel user = keycloakSession.users().getUserById(realm, userId);
            if (user != null) {
                switch (eventType) {
                    ....
                    case LOGIN:
                        try {
                            setUserTimeStamp(user, "lastLoginTimestamp");
                            handled = true;
                        } catch (DateTimeException ex) {
                            log.error("setUserTimeStamp: ", ex);
                        }
                        break;
                }
        }
    }
    
    
    private void setUserTimeStamp(UserModel user, String timestampName) {
        user.setSingleAttribute(timestampName,  ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT));
    }
0
Herman Kulik On

It turned out that KeycloakModelUtils.runJobInTransaction should include all the manipulation actions (even extractions from the session), and then it works.
Example:

KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), s -> {
    RealmModel realm = session.realms().getRealm(event.getRealmId());
    UserModel u = s.users().getUserById(realm, event.getUserId());
    u.setSingleAttribute(KEY, "value");
});