Skip to content

fix(groups): reconcile members on PUT and invalidate user cache#112

Open
nirclarity wants to merge 1 commit into
Metatavu:developfrom
nirclarity:fix/put-group-members
Open

fix(groups): reconcile members on PUT and invalidate user cache#112
nirclarity wants to merge 1 commit into
Metatavu:developfrom
nirclarity:fix/put-group-members

Conversation

@nirclarity
Copy link
Copy Markdown

@nirclarity nirclarity commented May 21, 2026

Summary

Two related fixes to GroupsController that block end-to-end group membership sync from Okta to Keycloak via SCIM.

Bug 1 — updateGroup silently drops members

The current implementation only updates displayName:

public Group updateGroup(ScimContext scimContext, GroupModel existing, ... Group group) {
    existing.setName(group.getDisplayName());
    return translateGroup(scimContext, existing);
}

Okta's Group Push uses PUT /Groups/{id} (not PATCH) with the desired final member list. With this no-op handler, the plugin returns 200 with the request "accepted" but membership changes never propagate to Keycloak — silently. Particularly nasty because Okta sees success and stops retrying.

(This is a separate-but-related issue to the path-less PATCH shape covered in #95 / #100.)

Fix: reconcile the request's members against the current members. Mirror the logic already in createGroup and patchGroup's REPLACE branch — remove users not in the desired set (with leave events) and add new ones (with join events).

Bug 2 — stale Keycloak user cache after membership change

After calling user.leaveGroup(...) / user.joinGroup(...), Keycloak's Infinispan user-side cache isn't invalidated. The join table in Postgres updates correctly, and getGroupMembersStream reads fresh, so /groups/{id}/members and the plugin's own SCIM GET /Groups/{id} view are correct. But /users/{id}/groups reads the user object's cached groups list and returns stale entries until the realm cache is manually cleared or the TTL expires.

This caused removed-from-group users to still appear in their groups list via the Admin REST API, even though SCIM had processed the removal correctly.

Fix: after each leaveGroup / joinGroup, explicitly evict the user from UserCache so subsequent reads re-read from DB.

Verification

Tested against Keycloak 26.6.0 + Okta SCIM provisioning (real Okta tenant + ngrok-exposed Keycloak):

  • Okta PUT /Groups/{id} adding a user → user appears in both /scim/v2/Groups/{id}.members and /admin/realms/{r}/users/{id}/groups immediately.
  • Okta PUT /Groups/{id} removing a user → both views update immediately (no cache clear needed).
  • Multi-user reconciliation (full member list replacement) works end-to-end.

Two related fixes to GroupsController:

1. updateGroup (PUT /Groups/{id}) previously only set displayName and
   silently dropped the `members` array. Okta's Group Push uses PUT
   (not PATCH) with the desired final member list, so this no-op meant
   membership changes from Okta never propagated to Keycloak. The fix
   reconciles the request's members against the current members:
   removes users no longer in the desired set (with leave events) and
   adds new ones (with join events).

2. After mutating membership via user.leaveGroup() / user.joinGroup(),
   Keycloak's Infinispan user cache holds a stale view of the user's
   group list. Subsequent reads of /users/{id}/groups return outdated
   entries until the cache expires. Explicitly evict the affected
   user from the UserCache to ensure immediate consistency.

Verified against Okta SCIM provisioning + Keycloak 26.6:
- PUT /Groups/{id} with added/removed members updates both the group's
  members list and each user's groups list.
- No manual cache clear required after membership changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

@nicolamacoir
Copy link
Copy Markdown
Contributor

Thanks for this fix!
Before merging this, could you please add a regression test?

@nirclarity
Copy link
Copy Markdown
Author

Thanks for this fix! Before merging this, could you please add a regression test?

Sure! I hope to get back to this soon :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants