Why SCIM needs OAuth 2.0 Client Credentials, not just bearer tokens
Static bearer tokens have been the default for SCIM auth for years. Here's why the client credentials grant is a better fit, and what changes when you use it.
Every SCIM integration starts with two systems agreeing to trust each other. An identity provider needs to create, update, and deactivate users in your app, automatically, without a human clicking through an onboarding flow every time someone joins or leaves a company. That's the entire premise of SCIM: turn user lifecycle management into an API problem.
But SCIM's spec has a quiet gap. It tells you exactly how to model a user, a group, a patch operation. It says almost nothing about how the identity provider should prove it's allowed to make those calls in the first place. RFC 7644 leaves authentication and authorization up to the implementer, recommending industry-standard protocols like OAuth 2.0 without mandating any particular mechanism. That ambiguity is reasonable given SCIM's job is to standardize a data model, not an auth flow. But it's also why almost every SCIM implementation defaulted to the simplest possible answer: a single, long-lived bearer token, generated once and pasted into the identity provider's config.
The bearer token default, and what it costs you
A static bearer token is easy to reason about. It's a string. You copy it into Okta or Entra, the IdP sends it in an Authorization header on every request, your SCIM server checks it against a stored value, done. For a first implementation, it's hard to beat.
The cost shows up later, and it shows up as standing risk rather than a single bad day.
A bearer token generated once and left in an IdP's admin console doesn't expire on its own. It doesn't rotate unless someone remembers to rotate it, and rotating it usually means generating a new value, going back into the IdP, pasting it in, and hoping nothing breaks in between. There's no built-in reason for the token to ever change, which means the token that was created on day one of a customer's provisioning setup is often still live years later. If it leaks (in a log line, a support ticket, a misconfigured proxy) it's valid until someone notices and manually revokes it. None of this is a flaw in any particular vendor's SCIM server. It's just what you get by default when the spec doesn't require anything better and the simplest option is a plain string.
What the OAuth spec actually offers here
OAuth 2.0 defines a grant type that maps almost exactly onto what SCIM needs. Section 1.3.4 of RFC 6749 describes the Client Credentials grant as the right shape for cases where the client is acting entirely on its own behalf, with no resource owner in the loop, and the scope of access is limited to resources already arranged in advance between the client and the authorization server. That's precisely the SCIM relationship. There's no end user authorizing anything at request time. The identity provider is the client, your SCIM server (or the authorization server in front of it) already knows which directory that client belongs to, and the whole exchange is machine to machine.
Mechanically, section 4.4 of the same RFC lays out the flow: the client authenticates directly to a token endpoint using its client ID and secret, requests an access token, and gets back a short-lived token it can use on subsequent calls. When that token expires, the client just repeats the exchange. No refresh token bookkeeping, no redirect, no user consent screen. It's a small, well-defined loop, and it's the reason Client Credentials has become the default grant type for service-to-service auth well outside of identity provisioning.
Applied to SCIM, this changes the risk profile substantially. Instead of one static string that's valid indefinitely, you get a client ID and secret that are exchanged for a token lasting minutes or hours. A leaked access token is only useful for a short window. A leaked client secret is still bad, but it's a credential you can rotate without breaking the sync, because the client secret and the access token are different things, and rotating the former doesn't require anyone to manually re-paste anything mid-flight.
Why this hasn't already happened everywhere
If Client Credentials is the obviously correct grant type for this problem, it's fair to ask why bearer tokens are still the norm. Part of the answer is that SCIM predates most of the SaaS industry's comfort with OAuth as infrastructure rather than an end-user login flow. But part of it is that adding Client Credentials support to a SCIM integration is more involved than it looks on paper, and the identity providers that have tried have run into real friction.
Some IdPs only support OAuth Client Credentials for their own pre-built app integrations in a catalog, not for custom SCIM connections, which is exactly the case where a service provider is most likely to need it. Others have shipped OAuth Client Credentials support for custom apps that turns out to send duplicate authentication methods (both a Basic Auth header and credentials in the request body) to the token endpoint, which strict, standards-compliant token servers reject outright, forcing admins back to bearer tokens or an on-premises agent as a workaround. These aren't edge cases dug up in obscure documentation. They're active threads on Okta's and Microsoft's own developer forums. The grant type is simple. Building a token endpoint and setup flow around it that actually works cleanly, per directory, across different IdPs, is the harder part.
How this works with WorkOS
At WorkOS we support Client Credentials for SCIM directories.
- Every directory connection gets its own OAuth client: its own client ID and secret, scoped to that one directory.
- The identity provider exchanges those credentials at a per-directory
oauth2/tokenendpoint for a short-lived JWT, uses that JWT to authenticate SCIM requests, and re-authenticates automatically once the token expires. There's no shared client across directories, so a compromised credential for one customer's directory doesn't expose anyone else's.
This is supported for Okta, Entra, and custom SCIM implementations. The Admin Portal's setup flow has a dedicated auth method step with instructions specific to each IdP, since the exact clicks differ between Okta's app integration screen and Entra's provisioning configuration. Credentials are shown once at setup, the same way you'd expect for any secret you shouldn't be storing in plaintext, and rotating a secret is a self-serve action rather than a support ticket or a full reconfiguration.
For an admin setting this up, the practical difference is that provisioning auth stops being a static value you set once and forget about, and starts being something you can rotate on a schedule (or immediately, if you need to) without taking the sync down while you do it. For a developer building on SCIM, it means directory-level credential isolation and short-lived tokens without having to hand-roll a token endpoint per customer.
The takeaway
SCIM never told anyone how to secure it, so the industry filled that gap with the easiest possible option, and mostly hasn't revisited it. OAuth 2.0's Client Credentials grant was designed for exactly this kind of machine-to-machine relationship, and applying it to SCIM directories fixes the two biggest weaknesses of a static token: it removes standing, unrotated credentials, and it makes rotation something you can do without disrupting the sync. If you're evaluating a SCIM implementation, whether you're building one or picking a provider to handle it for you, how it authenticates is worth asking about with the same seriousness as how it maps attributes or handles group sync.