Set it up
-
Turn the endpoint on. It is off by default and answers
404until theauth:scimsection is enabled: -
Create a SCIM client for the identity provider. The name is a lowercase label (
entra,okta, …); the token is returned once and only its hash is kept: -
Configure the identity provider with the base URL
https://<control host>/scim/v2and the token as a bearer token.
/scim/v2 lives at the root of the control listener, not under /api, and needs no session.
What maps where
Other core attributes (
name, a user’s displayName, phoneNumbers, addresses, roles, password, …) and extension attributes are accepted and not stored, so a default attribute mapping works unchanged.
A DELETE of a user deletes the account. Set "deleteDisables": true in auth:scim to disable it instead and keep its bindings and history.
Filters (eq, ne, co, sw, ew, gt, ge, lt, le, pr, and, or, not) work on id, userName, externalId and emails for users and on id, displayName, externalId and members.value for groups. Bulk operations, sorting and ETags are not supported.
What a SCIM client may do
A SCIM client acts only on the users and groups it created, which carry its name as their source:- It cannot read, change or delete an account or group an operator created, or one another client created. Such a request answers
404, and a write is recorded in the audit log as denied. - It cannot set roles, passwords or sign-in identities, and cannot reach roles, role bindings, teams, projects or service accounts.
- It changes only the attributes it maps (
userName,emails,externalId,active), so an operator’s change to an account, such as disabling it or removing a role, is never written back over. - It cannot disable or delete the last enabled admin. Such a request answers
409and is recorded in the audit log as denied. - Membership of its own groups is the identity provider’s call and is not checked against your own grants. What membership grants is decided by the role bindings you make on those groups.
- A new group never takes a name that an existing group holds or a role binding names, so it inherits no grant made for something else.
scim-client and the client’s name.
Signing in
SCIM creates the account without a password or sign-in identity. Relay expects the SCIM client and the configured OIDC provider to be the same identity provider, and links the account at its user’s first OIDC sign-in. When no account holds the sign-in’s identity (issuer and subject), relay looks for an account to link before it provisions a new one. It links the account when all of these hold:- a SCIM client created it, and that client is not revoked;
- it has no sign-in identity yet;
- its
externalIdequals the value of thelinkClaimclaim in the verified ID token.
users.update of identities. This works whether OIDC registration is open or closed. Later sign-ins find the account by its identity.
Set linkClaim in auth:scim to the claim that carries the id the identity provider sends as externalId. Only sub and oid are accepted: a claim the user can edit, such as an email, could link them to someone else’s account.
Entra’s default user mapping sends
mailNickname as externalId. Replace it with a direct mapping from objectId to externalId before the first user is provisioned, so externalId carries the value of oid.
- An email never links an account. A first sign-in whose email belongs to an account it does not link to is refused with
403. - A disabled account that matches is refused like any disabled account, and nothing is linked.
- If more than one account matches (two SCIM clients sent the same
externalId), the sign-in is refused with409and nothing is linked. - An identity provider that returns no ID token links nothing, because the claim is read only from the verified token.
- While
auth:scimis disabled, nothing is linked.
Rotating and revoking
GET /api/scim-clients lists the clients with the start of each token and when it was last used.
To rotate a token, call POST /api/scim-clients/<name>/rotate. It returns a new token once, and the old one stops working at once. The client keeps its name, so the users and groups it provisioned stay its own; put the new token in the identity provider:
POST /api/scim-clients/<name>/revoke stops the token for good; the users and groups it provisioned stay, still carrying its name as their source, and no client can change them. A revoked client is not rotated (409) and its name is not issued again (creating it answers 409), so no new token takes over what it provisioned.
Under RELAY_AUTHZ=rbac, managing SCIM clients (list, create, rotate, revoke) takes an admin: a custom role holding the scim-clients verbs is not enough. Changing the auth:scim section takes an admin in either mode.