EventListener with an update in the db?

I have an event listener and I’d like to update a custom attribute of the user

When doing so, I have an exception

keycloak_1  | 08:37:46,697 ERROR [io.undertow.request] (default task-5) UT005023: Exception handling request to /auth/realms/master/account/: javax.persistence.TransactionRequiredException: Executing an update/delete query
keycloak_1  |   at org.hibernate@5.3.20.Final//org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:398)
keycloak_1  |   at org.hibernate@5.3.20.Final//org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1641)
keycloak_1  |   at org.keycloak.keycloak-model-jpa@14.0.0//org.keycloak.models.jpa.UserAdapter.removeAttribute(UserAdapter.java:210)
keycloak_1  |   at io.maasify.keycloak-maasify//io.maasify.keycloak.events.AccountUpdateEventListenerProvider.accountUpdated(AccountUpdateEventListenerProvider.java:71)

Here is a simplified code of the event listener

public class AccountUpdateEventListenerProvider implements EventListenerProvider {
    private static String PHONE_NUMBER_VERIFIED_ATTR = "phoneNumberVerified";

    private KeycloakSession session;
    private RealmProvider model;
    private EventListenerTransaction tx = new EventListenerTransaction(null, this::accountUpdated);

    public AccountUpdateEventListenerProvider(KeycloakSession session) {
        this.session = session;
        this.model = session.realms();
        this.session.getTransactionManager().enlistAfterCompletion(tx);
    }

    @Override
    public void onEvent(Event event) {
        if (event.getType() == EventType.UPDATE_PROFILE) {
            if (event.getRealmId() != null && event.getUserId() != null) {
                tx.addEvent(event);
            }
        }
    }
    
    private void accountUpdated(Event event) {
        RealmModel realm = model.getRealm(event.getRealmId());
        UserModel user = session.users().getUserById(realm, event.getUserId());
        user.removeAttribute(PHONE_NUMBER_VERIFIED_ATTR);
    }
}

    @Override
    public void onEvent(AdminEvent event, boolean includeRepresentation) {        
    }

    @Override
    public void close() {
    }

}

Any pointers appreciated !

Thanks for your time

Replying to myself : if you want to exec in the same transaction, simply do not use EventListenerTransaction

public class AccountUpdateEventListenerProvider implements EventListenerProvider {
    private static String PHONE_NUMBER_VERIFIED_ATTR = "phoneNumberVerified";

    private KeycloakSession session;
    private RealmProvider model;

    public AccountUpdateEventListenerProvider(KeycloakSession session) {
        this.session = session;
        this.model = session.realms();
    }

    @Override
    public void onEvent(Event event) {
        if (event.getType() == EventType.UPDATE_PROFILE) {
            if (event.getRealmId() != null && event.getUserId() != null) {
                accountUpdated(event);
            }
        }
    }
    
    private void accountUpdated(Event event) {
        RealmModel realm = model.getRealm(event.getRealmId());
        UserModel user = session.users().getUserById(realm, event.getUserId());
        user.removeAttribute(PHONE_NUMBER_VERIFIED_ATTR);
    }
}

    @Override
    public void onEvent(AdminEvent event, boolean includeRepresentation) {        
    }

    @Override
    public void close() {
    }

}
1 Like