Provision users with SCIM
Connect your identity provider, the issuer, to Kairos. Then a user that you create, deactivate or group in the issuer gets the same change in Kairos. In particular, a deactivation in the issuer revokes the Kairos access of that person. Nobody has to remember to do it.
You need: org-admin access to the Kairos tenant, and admin access to the issuer’s SCIM application. The tenant must also be reachable from the issuer.
Mint a SCIM token
Section titled “Mint a SCIM token”curl -X POST https://<kairos-host>/api/scim-tokens \ -H "Authorization: Bearer $YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "okta-prod"}'This is an ordinary /api call. Thus, unlike the SCIM endpoints, it needs the
tenant resolved: use the tenant’s own host, or add -H "X-Tenant: <slug>".
Copy the token out of the response now. Kairos returns it once, and stores
only its SHA-256. If you lose it, revoke it and mint another —
GET /api/scim-tokens lists metadata but never secrets.
Name it after the issuer application it is for. You will want to know which token to revoke when you rotate.
Point the IdP at Kairos
Section titled “Point the IdP at Kairos”In the issuer’s SCIM application:
- Base URL:
https://<kairos-host>/scim/v2 - Authentication: HTTP header / bearer token
- Token: the one you just copied
No tenant subdomain and no X-Tenant header. The token itself carries the
tenant — see the SCIM reference for how Kairos parses it.
Make the identity join work
Section titled “Make the identity join work”This is the step that goes wrong, and it goes wrong quietly. Kairos binds an
inbound SCIM user to an existing user by externalId, then userName, then
email. A login binds by the OIDC sub.
Configure the issuer to send the same value as externalId that it puts in
the OIDC sub claim. Then the first login finds the user that SCIM created.
If the two values are different, the result depends on the email_verified
claim in the login token:
- The claim is
true. Kairos finds the SCIM-created user by email and the person logs in as that user. - The claim is absent, or has a different value. The person gets a second user row without the org membership. They can log in and appear to have no access.
Thus make sure that the issuer sends email_verified as true for each
provisioned user. Do this also when the two values agree. The
SCIM reference gives
the full rule.
If your issuer is Okta: userName defaults to the login email. Map the SCIM
app’s externalId to whatever the OIDC app emits as sub. Alternatively set
the OIDC app’s subject to Okta’s userName, which lets the email-shaped
userName match instead.
If your issuer is Entra ID: the default mapping of externalId is from
objectId. Thus make the OIDC app emit oid as sub. Otherwise adjust the SCIM attribute
mapping to match whatever sub you do emit.
If your issuer is something else: find what it sends as sub, and send the same
thing as externalId.
Users who have already logged in are safe either way. Their existing row keeps
its sub, and SCIM never overwrites the login join key. Only a first login
after a SCIM create can go wrong.
Push users before groups
Section titled “Push users before groups”Group membership can only reference users Kairos already knows. Most issuers push users first on their own; if yours lets you order the operations, do users first.
Two group names are meaningful, and Kairos accepts nothing else. Push only
these two shapes. Kairos refuses any other displayName with 400 invalidValue,
and does not skip it. Thus an issuer configured to push its whole group catalogue
reports a failure per group.
kairos-admins— membership promotes to organization admin, removal demotes to memberkairos-team-<slug>— membership is team membership, and creating the group creates the team and its delivery board. The slug must match^[a-z][a-z0-9_-]{1,62}$
You can delete a team group and then create it again with the same name. The
DELETE soft-deletes the team, and the slug becomes free. The new group is a
new team with a new id. It does not get the members or the board items of
the deleted team. Kairos refuses the name with 409 uniqueness only while a
live team has that slug.
Verify
Section titled “Verify”Deactivate a test user in the issuer, and let it sync. Then have them call any
/api endpoint with a token they already hold. They should get 403
MEMBERSHIP_REQUIRED rather than a successful response. Access tokens
validate locally until they expire, but Kairos checks membership per request.
Thus revocation takes effect immediately rather than at token expiry.
Then reactivate them and confirm access returns. Reactivation is a fresh
provision: the issuer must POST the user again. If yours reactivates by sending
PATCH {"active": true} to the id it stored, it gets a 404. The reason is that
the deprovision removed the resource and did not mark it inactive.
Limits to expect
Section titled “Limits to expect”- You cannot remove the last admin. Kairos refuses to demote, deactivate or
delete them, with
400 scimType: "mutability", so plan a second admin before you need one. - Kairos refuses renames. Rename a team through
/api/teams, not by renaming the SCIM group. - A team group will not delete while its board has work on it. The refusal
is
400 mutability, and it will keep failing until someone moves or deletes the cards through/api. Wind the team’s board down first — see Wind down a team. kairos-adminscannot be deleted.400 mutability; it is built in.- Deprovisioning keeps the user row and removes the membership, for audit. Re-activation is a fresh provision, not an undelete.
- Bulk, sorting, ETags and password operations are not supported, and
Kairos advertises that in
ServiceProviderConfig, so a conforming issuer will not attempt them. Kairos also does not implement/Me. But SCIM has no way to advertise that, so an issuer that calls it gets a plain 404. - Not every refusal is a SCIM error envelope. Kairos answers an unrouted
path, a bad
count, a wrong method or an oversized body before the SCIM layer — see the SCIM reference.
Related reading
Section titled “Related reading”- SCIM reference — the mapping table, the supported subset, filters and the error envelope
- Configure an OIDC issuer — the authentication half; SCIM handles lifecycle only
- Capabilities — what an organization admin can do that a member cannot