fix: defer ClientCreationEvent to after transaction commit [fj4WqyCCw3C5ShR1RfB7MoBPTpkRrBFYP1uT35g3MvT] - #51598
Conversation
…cloak#51594) The ClientCreationEvent was published synchronously inside JpaRealmProvider.addClient before the client representation (attributes, redirect URIs, protocol mappers, etc.) was fully populated by the caller. Any on:client-created workflow resource condition that inspects client state always saw a half-initialized client and silently returned false, preventing the workflow from ever activating. Defer the event publication to after the transaction commits using enlistAfterCompletion. By commit time the caller has finished applying the full representation, so conditions evaluate against the complete client state. Fixes keycloak#51594
There was a problem hiding this comment.
Pull request overview
Defers client-creation events until transaction completion so listeners can inspect fully populated clients.
Changes:
- Publishes
ClientCreationEventthrough an after-completion transaction callback. - Suppresses publication when client creation rolls back.
Suppressed comments (1)
model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java:952
- Publishing here runs while
DefaultKeycloakTransactionManageris already marked completed. A matching workflow reachesWorkflowExecutor.runTask(), whoseenlistAfterCompletion()call then throwsTransaction already completed;processEvent()catches that exception, so the workflow still never activates. Publish from a fresh transactional session with the client reloaded, or defer only workflow processing so work is enqueued in an active transaction.
public void commit() {
session.getKeycloakSessionFactory().publish(new ClientModel.ClientCreationEvent() {
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // client representation (attributes, redirect URIs, protocol mappers, etc.) | ||
| // is fully populated before any on:client-created workflow resource conditions | ||
| // are evaluated. See #51594. | ||
| session.getTransactionManager().enlistAfterCompletion(new KeycloakTransaction() { |
|
Thanks for picking this up — issue reporter here. Deferring the publish past client population is the right instinct, but I think The path:
Two smaller notes:
|
Fixes #51594
Problem
The
ClientCreationEventwas published synchronously insideJpaRealmProvider.addClientbefore the client representation (attributes, redirect URIs, protocol mappers, etc.)
was fully populated by the caller. Any
on: client-createdworkflow resource conditionthat inspects client state (e.g., a custom
WorkflowConditionProviderreading a clientattribute) always saw a half-initialized client and silently returned
false, preventingthe workflow from ever activating.
Root cause
JpaRealmProvider.addClientpersists the bareClientEntity(onlyid,clientId,enabled,standardFlowEnabled,realmId) and immediately publishes theClientCreationEvent. The caller — typicallyRepresentationToModel.createClient—applies attributes, redirect URIs, protocol mappers, etc. after
addClientreturns.Fix
Defer the event publication to after the outer transaction commits using
enlistAfterCompletion. By commit time the caller has finished applying the fullrepresentation, so any workflow conditions evaluate against the complete client state.
Verification
enlistAfterCompletioncallback runs after the main transaction commits(in
DefaultKeycloakTransactionManager.commit()), at which point the JPApersistence context is still open and the
ClientModelentity is still managed.(rolled back), so no spurious event is published for a failed client creation.
WorkflowExecutor.runTask→enlistAfterCompletion) isalready used elsewhere in the workflow subsystem.
Related
on: client-createdconditions are evaluated before the client is populated, so they can never match #51594UserCreatedWorkflowEventProviderreacts toEventType.REGISTER(user event) which fires after the user is fully populated — the fix makes the
client side consistent with this pattern.