A minimal SCIM 2.0 provider extension for Keycloak 26.6.4. Keycloak is the
source of truth: user, group, membership, and selected role changes are sent to
configured SCIM targets. It is a Kotlin rewrite inspired by
mitodl/keycloak-scim, under the
in.sunal.keycloak namespace.
config— realm component settings and the User Federation configuration providerevent— Keycloak event handling and dispatch to configured SCIM targetsclient— outbound SCIM HTTP transport and authenticationsync— user/group payload mapping and synchronization orchestrationpersistence— theSCIM_RESOURCEentity, repository, and JPA registration
- Creates use
POST /UsersorPOST /Groups. - Updates use full-resource
PUT /Users/{id}orPUT /Groups/{id}. - Deletes use
DELETE /Users/{id}orDELETE /Groups/{id}. - User roles are included only when the Keycloak role attribute
scimis exactlytrue. Roles inherited from direct group membership are included. - Groups contain direct user members only. Nested groups are not exported.
- A user or group with the attribute
scim-skip=trueis not synchronized. - Local-to-remote IDs are stored in the
SCIM_RESOURCEtable per realm and target component. - Realm and client role assignments are handled from both the standard Admin API and the Admin UI bulk-delete endpoint.
- A permanent-lockout event updates the remote user's
activestate. - Deleting a user refreshes mapped groups so their membership no longer relies on target-side delete cascading. Deleting a group or changing/deleting a role refreshes mapped users so inherited or renamed roles are removed.
This provider is outbound only. It never reads remote users or groups into Keycloak, and enabling it does not export all existing resources. It reacts to events that occur after enablement. An existing resource is created remotely when a later create/update/membership event first reaches the provider.
Delivery is best effort and each event makes one HTTP attempt. A timeout, connection failure, or non-success response is logged but never rolls back the Keycloak change. There is no automatic retry, durable queue, periodic reconciliation, or one-off export:
- A failed create stores no mapping; a later event can attempt creation again.
- A failed update keeps its mapping; a later event can attempt the
PUTagain. - A failed delete keeps its mapping, but because the local resource is already gone there may be no later event to retry it. Operators must inspect the log and delete the remote resource manually or otherwise re-drive the operation.
- A remote
404during delete is treated as success and removes the mapping.
Some uncommon cleanup events can fan out. User deletion refreshes all mapped
groups for that target; group deletion and role updates/deletions refresh all
mapped users. These passes only update entries already present in
SCIM_RESOURCE: they are not an initial export and do not create unrelated
existing users or groups. A failure for one refreshed resource is logged and
does not stop the remaining resources.
Nested groups, organizations, LDAP/import reconciliation, retries, and an outbox remain outside this provider's scope.
Enable the scim event listener in the realm, then add a scim entry under
User Federation. Each entry represents one independent outbound target and can
enable or disable user and group propagation separately.
Configuration supports:
- SCIM 2.0 base endpoint
- no authentication, bearer-token authentication, or HTTP Basic authentication
- connection and request timeouts
Credentials are stored in the Keycloak component configuration. The bearer token/basic password field is rendered as a password field, but this provider does not integrate with the Keycloak vault.
Build with JDK 21:
gradle clean test integrationTest shadowJarCopy build/libs/keycloak-scim-1.0.0.jar to /opt/keycloak/providers/, then
rebuild the Keycloak installation:
bin/kc.sh buildLicensed under the Apache License 2.0. See NOTICE for upstream attribution.