SCIM Directory Sync
Per-account SCIM 2.0 configuration for automated user and group provisioning from a customer's IdP.
Configuration model
Each Account can have a ScimConfig row describing how its customer's identity provider (Okta, Entra ID, etc.) should push directory changes into RealmSSO.
| Field | Purpose |
|---|---|
enabled | Whether SCIM sync is turned on for this account |
endpoint | The SCIM base URL published back to the customer |
bearerTokenHash | The SHA-256 hash of the token the customer's IdP authenticates with. Requests are matched against this hash; the raw token is never persisted (the legacy plain bearerToken column is left unwritten — see Encryption at Rest) |
attributeMapping | JSON mapping of SCIM attributes to RealmSSO/Keycloak user fields |
autoProvision | Create a user automatically on first SCIM push (defaults to true) |
autoDeprovision | Deactivate a user automatically on SCIM delete (defaults to false) |
groupSync | Sync SCIM groups as well as users (defaults to false) |
lastSyncAt / lastSyncStatus | Bookkeeping for the most recent sync attempt |
Endpoints
SCIM routes are mounted at /api/scim/v2 (see Architecture Overview for why that needs its own content-type parser). Every request authenticates with a bearer token looked up against ScimConfig.bearerTokenHash — there's no account id in the path, the token alone identifies the tenant.
/api/scim/v2/Users/api/scim/v2/Users/api/scim/v2/Users/:id/api/scim/v2/Users/:id/api/scim/v2/Users/:id/api/scim/v2/Groups/api/scim/v2/Groups/api/scim/v2/Groups/:id/api/scim/v2/Groups/:id/api/scim/v2/ServiceProviderConfig/api/scim/v2/SchemasWhat the routes do, and what the defaults mean
The SCIM routes persist. A push writes ScimUser, ScimGroup, and ScimGroupMember rows in RealmSSO's own database, and — when the matching config flag is on — creates, updates, or deactivates the corresponding object in the account's Keycloak realm. attributeMapping is resolved against the incoming body, and autoProvision, autoDeprovision, and groupSync are each read at the point they apply. Every request also records an observability event (scim.provisioned, scim.updated, scim.deprovisioned, and their group equivalents).
The defaults are the part worth reading twice. A fresh ScimConfig has autoProvision: true but autoDeprovision: false and groupSync: false. Out of the box, therefore, a customer's IdP creates users in Keycloak but deactivating a user in their directory does not deactivate the Keycloak account, and group membership is not mirrored into the realm. If offboarding through the directory is the reason you want SCIM, you must set autoDeprovision yourself.
Why the response shapes matter
The contract has to be exactly right before any of the provisioning above is reachable. /ServiceProviderConfig and /Schemas are what an IdP's SCIM connector introspects to decide whether patch, filtering, and bulk operations are supported, and both are unauthenticated for that reason. application/scim+json parsing also has to work, or Fastify rejects every SCIM write with a 415 before a handler ever runs — see Architecture Overview for that content-type parser. A connector that mis-reads either one will not attempt the calls that do the work.