Safeguard
Application Security

Just-in-Time Provisioning Moves Trust From a Person to a Claim in a Token

A new employee signs in with SSO for the first time, and your application creates an account and assigns a role based on group claims from the identity provider, with no human in the loop to notice if the claim maps to more access than intended.

Priya Raman
Staff Security Engineer
7 min read

A new employee signs in with single sign-on for the first time. Your application has never seen them before, so it creates an account on the spot, using whatever the identity provider's response says about who they are: their name, their email, and usually a set of group memberships or role claims that your application reads to decide what access to grant.

This is just-in-time provisioning, and it removes an entire category of manual account setup. It also means the identity provider's claims are now your source of truth for authorization, at the exact moment an account is created, with no human in the loop to notice if something in that claim looks wrong.

This post is about the specific ways that trust gets misplaced. For whoever wired up SSO and assumed the group claims could simply be read and mapped.

The claim is not always what it appears to be

Group and role claims are configured by whoever set up the SSO integration on the identity provider side, which for many organisations is IT rather than security, and the mapping between an identity provider's group name and the permission your application grants is a piece of configuration that can drift, be misconfigured from the start, or be broader than intended without anyone specifically reviewing it as an access control decision.

A user can sometimes belong to a group for reasons unrelated to the access your application infers from it. A company-wide "All Employees" group, added to a new SSO configuration as a convenient default because it captures everyone who should have basic access, can end up mapped to a role more permissive than intended if the mapping was built quickly rather than deliberately, and the person doing the mapping was thinking about who should get in rather than exactly what they should get once they do.

Nested or transitive group memberships are not always resolved consistently. Whether a user who belongs to a subgroup is treated as also belonging to its parent group depends on how your identity provider computes and returns group claims, and an integration built assuming flat, explicit membership can silently grant broader access than intended once nested groups are introduced later, by someone who had no reason to think about how your application interprets the claim.

The first-login race

Just-in-time provisioning typically creates the account, and assigns its initial role, based on the claims present at that first successful authentication. This is usually a reasonable design and it has a specific edge case worth knowing about: if a user's group membership is in the process of being changed, added to a more privileged group as part of an onboarding workflow that has not yet fully propagated, or removed from one as part an offboarding step that is still in progress, the exact moment of first login determines what gets baked into that new account, and it may not reflect the intended end state.

This matters most for accounts that are provisioned automatically as part of a larger workflow, where a user's group memberships are being set up by an automated process running concurrently with, rather than strictly before, their first application login.

What happens to the account afterward

Provisioning is usually easier to build correctly than ongoing synchronisation, so many implementations create the account and its initial role at first login and then never fully re-derive that role from subsequent claims. A user's group membership changes at the identity provider weeks later; whether your application's stored role changes to match depends entirely on whether anyone built the re-synchronisation logic, which is a separate and less immediately visible piece of work than the initial provisioning flow.

If your application only re-checks group membership at each login, rather than on some other trigger, a user whose access was reduced at the identity provider retains their old, more permissive role for as long as their existing session remains valid, which for a long-lived session or a remember-me token can be considerably longer than anyone administering the identity provider would assume.

An account provisioned once and never revisited becomes exactly the kind of orphaned, unreviewed account this entire category of problem describes elsewhere, except that in this case it was created automatically, by design, rather than through the kind of manual signup that at least prompts someone to eventually ask who it belongs to.

What to actually build

Re-derive role and group-based access on every login, not only at first provisioning, so that a change in the identity provider's claims is reflected the next time the user authenticates, rather than persisting indefinitely from whatever was true the first time they signed in.

Map identity provider groups to application roles explicitly and narrowly, treating the mapping itself as an access control decision that deserves the same review as any other permission grant, rather than as a piece of SSO configuration that only IT needs to think about. A broad, convenient default group mapped to more access than intended is a privilege grant, whoever configured it and whatever their reason.

Log the specific claims used to provision or update an account at each login, so that if a user's access looks wrong later, you can determine whether it came from the identity provider's claims at that moment or from something that persisted in your own system afterward, which are two different problems with two different fixes.

Set a maximum session lifetime that forces re-authentication, and therefore re-derivation of claims, on a cadence that matches how quickly you expect access changes at the identity provider to need to take effect. A session that never expires is a session that never re-checks, regardless of how frequently the identity provider itself is updated.

Check yours

Ask, specifically: if a user is removed from a privileged group at the identity provider right now, when does your application actually reflect that. At their next login. The next time their session expires. Never, until someone manually intervenes. The honest answer to this question, tested rather than assumed, is the actual latency of your access control system, regardless of how quickly the identity provider itself propagates the change.

The concession

Re-deriving role from claims on every single request, rather than at login, is usually unnecessary overhead for the marginal security benefit it provides over re-deriving at each new session, and full real-time synchronisation with the identity provider, reacting to group changes the instant they happen rather than at the next login, is genuinely more infrastructure than most applications need to build, particularly if session lifetimes are already reasonably short.

The proportionate version scales with session lifetime and with the sensitivity of what a role controls: short sessions with re-derivation at each new login cover most cases adequately, and the additional investment in real-time synchronisation is worth making specifically for the roles that control the most consequential access, rather than uniformly across every role your application has.

The implication

Just-in-time provisioning moves the moment of trust from a person reviewing an access request to a claim in a token, evaluated automatically, at the exact instant an account first exists, and every subsequent question about whether that account's access is still correct depends on whether anything re-evaluates that claim afterward.

Ask when your own application last checked whether a user's identity provider group memberships still match what their account was granted. If the honest answer is at first login and never again, that is the gap, and it is invisible until someone is removed from a group and nothing downstream ever finds out.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.