SCIM 2.0 Inbound Provisioning — Operator Guide
Obscura can let your identity provider (Okta, Microsoft Entra ID / Azure AD, OneLogin, or any
SCIM 2.0-compliant IdP) create, update, and deactivate Obscura accounts and push group
membership automatically over the standard SCIM 2.0 protocol (RFC 7643 / 7644). When someone is
added to your directory they appear in Obscura; when they change teams their Obscura roles follow;
when they are offboarded their Obscura account is disabled — all without an administrator touching
Obscura.
SCIM provisions accounts; it does not log anyone in. Accounts created by SCIM have no
password (provider='scim'). Users sign in through your OIDC single sign-on — the same IdP —
which links to the SCIM-provisioned account by email. Think of SCIM as the lifecycle channel
(who exists, what groups they are in) and OIDC SSO as the authentication channel (how they log
in). You configure both against the same IdP.
SCIM is disabled by default. There is no environment variable and no config file change —
nothing happens until an administrator mints a SCIM token inside Obscura. With no active token,
every /scim/v2 request is rejected with 401, the SCIM tables stay empty, and the deployment is
byte-for-byte its non-SCIM self. This document is what to hand whoever administers your IdP: the
exact values to paste, the attribute mapping, and how to diagnose the common failure modes.
1. What SCIM does in Obscura
- Provision (create). When the IdP pushes a new user, Obscura creates the account with
provider='scim'and no local password, using the pushed email and display name. The IdP's
externalIdis stored as a correlation identity so future updates target the same account. - Update. Profile changes (display name, email) pushed by the IdP update the Obscura account.
- Deprovision = disable, never delete. Deactivating a user in the IdP (SCIM
active=false) — or
a SCIMDELETE— disables the Obscura account: it sets the account to disabled, signs the
user out of every active session, and strips the roles SCIM granted. The account row, the
documents they authored, and the audit trail all survive. Re-activating the user in the IdP
(active=true) restores the account and re-applies their group-derived roles. Obscura never
hard-deletes a provisioned account, so a laterGET /Users/{id}still returns it with
active:false. - Group membership → Obscura roles. The IdP pushes groups and their members; you map each
pushed group to an Obscura role (in Admin → Directory). Membership changes re-grant and revoke
those roles automatically. Roles you assign manually in Obscura are never touched by SCIM.
Authentication is not part of SCIM. A provisioned user cannot log in until they authenticate
through your OIDC SSO (see §7 and the "user can't log in" row in §8).
2. Enable SCIM in Obscura (administrator, one-time)
- In Obscura, go to Admin → Directory.
- In the SCIM card, click Generate token. The bearer token is displayed once in a
copyable field — copy it now and store it in your IdP's secret store. It is never shown again;
Obscura keeps only a one-way hash. If you lose it, mint a new one (§6). - Note the SCIM base URL shown on the same card:
https://<your-obscura-host>/scim/v2
That is the entire Obscura side. Generating a token is what enables SCIM — the presence of an
active token means SCIM is on. The card also shows live status: whether SCIM is enabled, when the
token was last used by the IdP, and how many accounts have been provisioned.
3. Configure your identity provider
Give these values to whoever administers your IdP. The steps are the same for Okta, Entra, and
OneLogin; the field labels differ slightly per product.
| Setting (Okta / Entra label may differ) | Value |
|---|---|
| SCIM connector base URL | https://<your-obscura-host>/scim/v2 |
| Authentication | HTTP Header — OAuth Bearer Token |
| Bearer token | the token minted in Admin → Directory → SCIM (§2) |
| Unique identifier field for users | userName |
| Supported provisioning actions | Push New Users, Push Profile Updates, Push Groups, Deactivate Users |
Notes:
- Obscura maps the SCIM
userNameto the account email — set the IdP's "Unique identifier
field for users" touserNameand populateuserNamewith the user's email (this is the Okta/Entra
default). - The IdP will probe the discovery endpoints on connect —
GET /scim/v2/ServiceProviderConfig,
/ResourceTypes, and/Schemas— to learn what Obscura supports. These require the bearer token
like every other endpoint. A correct base URL + token is all that is needed for the test/verify
step in the IdP's connector setup to pass. - Every request must carry
Authorization: Bearer <token>. A request with no token, a wrong token,
or a revoked token gets401.
4. Attribute mappings
Obscura reads only the attributes below. Anything else the IdP sends is accepted and ignored (no
partial-failure). Leave the IdP's other default mappings in place; they do no harm.
| SCIM attribute (from the IdP) | Obscura field | Notes |
|---|---|---|
userName |
account email (login) | The unique identifier. Used as the email when no primary email is sent. |
emails[primary].value |
account email | Preferred source of the email; falls back to the first emails entry, then to userName. |
displayName |
display name | |
name.formatted |
display name | Used when displayName is absent. |
active |
enabled / disabled | true = enabled; false = disabled + sessions revoked + SCIM roles stripped. Defaults to enabled if omitted. |
externalId |
IdP correlation id | Stored as the account's SCIM identity subject so updates target the same account. Falls back to the email if the IdP omits it. |
For groups, Obscura reads displayName, externalId, and members (each member's value is
an Obscura user id returned when the user was provisioned).
5. Push groups → Obscura roles
- In the IdP, enable Push Groups and select the groups whose members should receive Obscura
roles. The IdP creates each group in Obscura and keeps its membership in sync. - In Obscura, go to Admin → Directory → SCIM → Group → role mapping. The pushed groups appear
in a picker (they only appear after the IdP has synced them — until then the card shows a
hint to that effect, because a SCIM group id is an opaque identifier, not something you type by
hand). - Map each group to an Obscura role and save.
From then on, membership changes reconcile automatically: a user added to a mapped group is
granted the role; a user removed from it (or whose group's mapping you delete) has that role
revoked. This mirrors the LDAP group→role machinery and shares nothing with it — SCIM and LDAP
keep separate mappings and separate grant ledgers, and a role granted by both is fine. Roles you
assigned manually in Obscura have no SCIM ledger entry and are never revoked by reconciliation.
6. Rotate or revoke the token
Both controls live in Admin → Directory → SCIM:
- Rotate / Generate issues a new token and immediately invalidates the previous one. Only
one token is ever active. After rotating, update the token in your IdP — until you do, the IdP's
SCIM calls will start returning401. - Revoke invalidates the active token and disables SCIM entirely: every
/scim/v2request
returns401and provisioning stops. Already-provisioned accounts are unaffected — they stay
exactly as they are (revoking the token does not disable, delete, or unlink anyone). Re-enable by
generating a new token and pasting it into the IdP.
7. Authentication — SCIM provisions, OIDC SSO logs in
SCIM never sets a password and is not a login method. To let provisioned users sign in,
configure OIDC SSO against the same IdP. On a user's first OIDC login, Obscura matches the
SCIM-provisioned account by email and attaches the OIDC identity to it — no duplicate account is
created. The result: the IdP provisions the account and its group-derived roles via SCIM, and the
same user then logs in through SSO with those roles already in place.
If you enable SCIM but not OIDC SSO, accounts will be created and kept in sync but nobody
provisioned-by-SCIM will be able to log in (they have no password and no SSO path). This is the most
common "it's not working" report — see §8.
8. What is NOT supported (by design)
Obscura implements the inbound-provisioning subset of SCIM 2.0 that Okta and Entra actually drive.
The following are intentionally not supported, and ServiceProviderConfig advertises them as
unsupported so a well-behaved IdP will not attempt them:
- Bulk operations (
/Bulk) — the IdP provisions one resource per request. - Sorting — results are returned in a stable order;
sortBy/sortOrderare ignored. - The
/Meendpoint — there is no authenticated end-user context on the SCIM channel. - ETags / resource versioning — no
meta.version, no conditional requests. - SCIM-carried passwords (
changePassword) — accounts have no password; login is via OIDC SSO. - SCIM as a login method — provisioning only.
- Rich filters. Only the exact form
attribute eq "value"is accepted, restricted to
userName eq/externalId eqon Users anddisplayName eqon Groups. Paging is
startIndex+count(up to 200 per page); there are no cursors. Any other filter operator or
attribute returns400 invalidFilter.
9. Troubleshooting
SCIM errors are returned to the IdP as standard RFC 7644 error envelopes (application/scim+json,
with a numeric status and, where applicable, a scimType). Most IdPs surface these in their
provisioning log or "sync errors" view.
| Symptom (in the IdP's SCIM log) | Likely cause | Fix |
|---|---|---|
Every SCIM request → 401 |
No active token (SCIM disabled), or the IdP is sending an absent / wrong / rotated / revoked token. | Confirm a token is active in Admin → Directory → SCIM; re-mint it and paste the new value into the IdP. Ensure the IdP sends Authorization: Bearer <token>. |
404 on GET/PUT/PATCH/DELETE of a user or group |
Wrong base URL or path, or the resource id doesn't exist here (never provisioned, or from a different Obscura). | Verify the connector base URL is exactly https://<your-host>/scim/v2; let the IdP re-sync so it holds current Obscura resource ids. |
409 (scimType: "uniqueness") on create |
A user with that userName / email — or that externalId — already exists, including a pre-existing local, LDAP, or OIDC account with the same email. |
Reconcile the duplicate: remove/rename the conflicting account, or match the IdP's userName to the existing email so the OIDC link is used instead. |
400 (scimType: "invalidFilter") |
The IdP sent an unsupported filter. Only userName eq / externalId eq (Users) and displayName eq (Groups) are supported. |
Adjust the IdP's provisioning settings so it filters only on userName (its default). Don't enable custom filter attributes. |
| User provisioned but has no roles in Obscura | The group isn't pushed yet, or there is no Group → role mapping for it. | Enable Push Groups in the IdP and confirm the group synced; then map it in Admin → Directory → SCIM → Group → role mapping. |
A deprovisioned user still appears in GET /Users/{id} |
Expected. Obscura disables, never hard-deletes. | None — the record correctly returns active:false; the account and its content/audit are retained and can be restored by re-activating in the IdP. |
| User can't log in after being provisioned | SCIM does not set a password — provisioning alone doesn't grant a login. | Configure OIDC SSO against the same IdP (see §7). On first OIDC login Obscura links to the SCIM account by email. |
For a quick Obscura-side sanity check independent of the IdP, any HTTP client works — substitute
your host and the minted token:
curl -H "Authorization: Bearer <token>" \
-H "Accept: application/scim+json" \
https://<your-obscura-host>/scim/v2/ServiceProviderConfig
A 200 with patch.supported=true, filter.supported=true, bulk.supported=false, and
changePassword.supported=false confirms SCIM is enabled and the token is valid. A 401 means the
token is absent, wrong, or revoked. The same call with no Authorization header should always
return 401 — that is the disabled-by-default posture doing its job.