Ndovu elephant mark ndovuai lab / SCIM: Playgrounds, Specs & Guides / SCIM, explained

NDOVU · AUDIO EXPLAINER · LISTEN

SCIM, explained

6:01·6 chapters·transcript
0:00 / 6:01

Keys: space/K play-pause · J/L jump 10s · arrows on the waveform nudge 5s

01 - Chapters

6 chapters

02 - Transcript

Read along

The full briefing transcript, timed to the audio. Click any line to jump there; the current line lights up while you listen.

HostThere is a user account in an application somewhere, fully formed: the right groups, the right attributes, the right lifecycle state. Its owner has never signed in. And one Tuesday, without anyone logging in anywhere, that account gets switched off.

HostNothing about that is single sign-on. Sign-on answers one question: may this session start? Provisioning answers a different question: which accounts exist, with which attributes, and in which state? That second question is what SCIM - S, C, I, M, pronounced sim - the System for Cross-domain Identity Management, exists to answer. In the next few minutes: what push provisioning actually is, the three parties that carry it, and the two identifiers whose authorship quietly decides whether your integrations survive contact with real identity providers.

HostSCIM moves identity data between systems over HTTP. The name you will hear in product meetings, "directory sync", is the same idea wearing a product shape: a system continuously exports the directory, or a scoped slice of it, to an application, and translates every lifecycle event into that application's own vocabulary. SCIM is the IETF protocol form of that push. The concepts RFC, 7642, defines push mode explicitly, and it is deliberately small. The wire contract lives in a second RFC, 7644, and the data model in a third, 7643.

HostThe word push carries more weight than it seems to. The identity provider initiates every state change: create, update, deactivate, delete. Changes made inside the application's own store never flow back as SCIM. The push is one-directional, and everything else about the protocol follows from that one fact.

HostThe contrast that makes SCIM make sense is just-in-time provisioning, the thing you get for free under SAML or OIDC. There, the identity provider's assertion creates the account at first sign-in, and the account is whatever that one assertion said. Three consequences follow, and they are all limitations.

HostNo account exists before the first sign-in, so group membership and application-level state cannot be prepared in advance. No flow ever removes the account, so deprovisioning depends on whatever lifecycle machinery the application grew on its own. And every sign-in rewrites the attributes from the assertion, so the authority over an attribute keeps flip-flopping between the assertion and whatever else writes to the store. SCIM's push model exists to make those three things explicit, decided before the user arrives, and reversible after the fact.

HostOn the wire, three parties do the work. The SCIM client is the identity provider's provisioning service: Entra's provisioning engine, Okta's provisioning integration, an HR-driven system. The SCIM service provider is the application's endpoint: the Users and Groups resources, plus the discovery endpoints, that accept the pushed data. And the subject is the user, or, since RFC 9944, the device, that the records describe.

HostHere is the property that shapes every integration: the client initiates every change, and each client speaks the protocol in its own dialect. A server that wants to be provisioned by four providers is not conforming to four readings of one RFC by accident. It is engineering for the differences on purpose. Underneath the SCIM endpoint, the application's own store never speaks SCIM at all. The service provider is a facade that translates, and most of the quirks you will meet in production, cursor storage under offset paging, attributes with no backing field, group membership that only resolves one way, are translation artifacts, not protocol violations.

HostNow the part that decides everything downstream: identifiers. The core schema RFC, 7643, assigns authorship of the two of them, and the assignment is absolute. The field called id is server-issued: case-exact, read-only, always returned, and the client must never specify it. The field called externalId is client-issued: optional, read-write, and the server must treat it as scoped to the provisioning domain. The client never picks the server's identifier, and the server never second-guesses the client's own.

HostSo how do the two sides correlate? That is the client's problem, and every provider solves it with a matching attribute: the one it filters on when it looks for an existing resource before creating or updating one. The providers' documented allowlists differ sharply. Entra matches on the user name and work email attributes. Okta requires a filter on the user name, the exact one, or provisioning does not work at all. AWS accepts fixed attribute combinations only. GitHub's enterprise contract ties the identity provider's object identifier to the external identifier, and if they disagree, the single-sign-on and provisioning links fail.

HostThe choice precedes everything else, because a matching key that the source directory can rename is a key that turns a rename into a duplicate account on the next correlation pass, or into a deletion when the client reads "no match" as "gone". The same wrong choice is how lifecycle flows end in destroyed data.

HostThree things to carry out of this. First: provisioning is not authentication. Single sign-on decides whether a session may start; SCIM decides which accounts exist at all. Second: the push is one-directional and the client initiates every change, so a service provider is a facade that absorbs each client's dialect, and the differences are engineered, not accidental. Third: the two identifiers have fixed authors. The server owns id, the client owns externalId, and the matching key that joins them is the single decision that precedes every lifecycle flow. The rest of this series takes each piece one at a time: filters, PATCH, schemas, and errors.

0:00