the token that lives forever
your SSO logout doesn't log anyone out. it just clears a cookie.
The companies, people and numbers in the scenarios below are illustrative composites of common situations. They are not real cases or measured results. Anything presented as data names its source.
A security engineer at a mid-market fintech was investigating an unusual access pattern in late 2025. A user account, an analyst who had left the company six months earlier, was making API calls to the company’s Salesforce instance. The account had been properly deprovisioned. SSO no longer authenticated the user. The HR system had closed out the record. From every identity-system perspective, the analyst was gone.
The API calls were hitting Salesforce through an OAuth refresh token the analyst had granted to a third-party reporting tool eighteen months earlier. The reporting tool had been authorized to access Salesforce data through her account. That authorization was stored as a refresh token with a long lifetime. The token didn’t know the analyst had left the company. The token only knew it was valid, and it continued to exchange itself for access tokens on schedule.
The SSO deprovisioning had correctly removed the analyst’s ability to log in. It had not revoked any of the OAuth grants she had issued during her tenure. Those grants sat in the Salesforce OAuth authorizations table, signed by a credential that was no longer valid for login but still valid for token refresh.
This is not a bug in Salesforce, or in the reporting tool, or in the SSO. This is the default behavior of the OAuth specification as implemented across the enterprise. Token lifetimes are decoupled from the identity lifecycle they were issued against. Revoking the identity does not automatically revoke the tokens the identity granted.
This essay is about the token layer, what it is, why it’s separate from the identity layer, and why most of your “deprovisioned” users still have working access to some of your systems through tokens nobody is tracking.
the authentication event is a moment. the session is a duration.
The mental model most people hold about authentication is that logging in grants access and logging out revokes it. The picture is: you log in, you do work, you log out, your access is gone.
The reality, in every modern web application, is that the authentication event produces tokens. The token is what the application checks to authorize subsequent requests. The login event is a moment. The token is valid for a period of time defined by the application, minutes, hours, days, or in some cases years.
The “logout” operation typically does two things. It clears the session cookie in the browser. It notifies the identity provider that the session has ended. It does not, in most cases, proactively invalidate tokens that have been issued against the identity. Those tokens remain valid until their natural expiration.
For short-lived tokens, access tokens with a one-hour lifetime, for example, this is mostly fine. The window between logout and natural expiration is small. A leaked access token is only exploitable for an hour.
For longer-lived tokens, refresh tokens, API tokens, OAuth grants, the window is much larger. A refresh token with a 30-day lifetime remains valid for 30 days after you logged out. A refresh token with a 90-day rolling lifetime remains valid indefinitely, as long as it’s used. An OAuth grant with a refresh token attached is effectively permanent until it is explicitly revoked.
The SSO logout, which clears the authentication cookie, has no effect on any of these. The tokens are separate artifacts stored in separate systems.
a login is a moment. a token is a duration.
- 1sign-inSingle sign-on checks who you are, once.
- 2sessionA cookie keeps you in for hours or days.
- 3OAuth grantAn app gets a refresh token that can last far longer than the job.
- 4after you leaveSSO is disabled. The app keeps exchanging its token for access.
the OAuth app authorization is the permanent grant
The most common version of this pattern is OAuth application authorization. A user authorizes a third-party application, Slack, Zoom, Zapier, a reporting tool, an AI assistant, anything that integrates with their primary work accounts, to access their data on an ongoing basis.
The authorization is stored as a grant in the resource system. Google Workspace, Microsoft 365, Salesforce, GitHub, and every other major SaaS platform maintains a list of OAuth grants for each user account. Each grant specifies the application, the scopes granted, and the tokens issued.
The tokens are long-lived. Refresh tokens in OAuth implementations commonly have no expiration, or have sliding expiration windows that renew every time the token is used. As long as the application makes a call on schedule, the token never expires.
When the user leaves the company, their primary account gets deprovisioned. Their SSO credentials are revoked. They can no longer log in. But the OAuth grants they authorized during their tenure remain in the grant tables of every system they authorized. The applications holding the refresh tokens continue to exchange them for access tokens. The access continues.
This is well-documented in the specifications and poorly understood in practice. Most IT and security teams assume that disabling a user’s SSO login disables everything the user had access to. It does not. SSO governs the user’s ability to authenticate interactively. It does not govern the applications that were granted token-based access while the user was active.
A mid-market company with 200 employees and typical SaaS usage has, conservatively, a few thousand OAuth grants across its environment. Most of those grants are tied to current employees. A meaningful fraction are tied to departed employees whose accounts have been deprovisioned, but whose grants were never revoked.
revocation requires explicit action in each system
The fix is straightforward to describe. When a user is deprovisioned, every OAuth grant they authorized needs to be revoked. Every API token they generated needs to be invalidated. Every long-lived credential issued against their account needs to be canceled.
The fix is hard to implement because it requires explicit action in each resource system. There is no single “revoke all” operation that propagates across the SaaS stack. Revoking Google OAuth grants requires calling Google’s API or using the admin console. Revoking Microsoft OAuth grants requires the same for Microsoft. Revoking Salesforce connected app authorizations requires yet another admin action. GitHub OAuth tokens, another. Each system has its own grant table and its own revocation API.
In mature environments, this is sometimes wired into a central offboarding automation. The automation, triggered by SCIM deprovisioning, calls a series of revocation endpoints across integrated SaaS systems. Where this works, it works well. Where it doesn’t work, in the vast majority of mid-market companies, deprovisioning clears the SSO layer and leaves the token layer intact.
The specific operational test is simple. Pick three departed employees from the last twelve months. For each of them, query the OAuth grants table in your primary SaaS systems (Google Workspace, Microsoft 365, Salesforce, GitHub, whichever you use). Ask: are there grants from these users that are still present? Are any of them still being used?
The answer, in most mid-market companies running this test for the first time, is yes to both questions for a meaningful number of the grants. The tokens are there. Some of them are being exchanged actively, which means something somewhere is still using them.
what deprovisioning in single sign-on ends, and what it doesn't
ends
when SSO is disabled
- New sign-ins through single sign-on
- Access to apps that check SSO on every request
keeps working
until revoked in each system
- Refresh tokens held by connected apps
- Personal access tokens and API keys
- Sessions that haven't expired yet
the personal access token problem
A specific subcategory worth calling out: personal access tokens (PATs) in developer tools.
GitHub PATs, GitLab PATs, and similar tokens in other developer tools are long-lived credentials issued directly by users for scripts, automations, and integrations. The tokens are tied to the user’s account. The tokens do whatever the user can do.
PATs have historically had no default expiration, although most providers have since added configurable expiration. In practice, most PATs in active environments were issued before expiration became the default, and they remain valid indefinitely. When the user who issued the PAT leaves the company, the PAT’s validity depends on whether the user’s account was fully deleted (which would invalidate the tokens) or merely disabled (which often does not).
A specific failure mode: a developer who leaves a company has PATs for GitHub, for Jenkins, for internal APIs, for every system they integrated with during their tenure. Some of these tokens are checked into CI/CD configurations that are still running. The CI/CD system uses the token on schedule. The system never notices that the issuing user is gone, because the token is valid and the system has no concept of checking the identity provider for account status.
The compromised ex-employee scenario: a disgruntled former developer retains their personal copies of PATs they issued while employed. Some of those PATs, if the company never rotated them, are still valid against company systems. The developer can use them from any device. The SSO has no visibility into this, because PATs bypass SSO by design.
The fix requires inventorying PATs and either rotating them to owned credentials or revoking them outright at deprovisioning. Few companies do this as part of routine offboarding. Fewer still run periodic audits of issued PATs against current employee status.
the session token edge case
Session tokens are the short-lived version of this problem. A user is logged into a SaaS application through their browser. Their session cookie is valid for, say, 24 hours. The user leaves the company at 2 p.m. on a Friday. By 3 p.m., HR has fired the deprovisioning workflow. By 3:15, the SSO has revoked the user’s credentials.
The user’s laptop, if it was not recovered, still has an active session in whatever SaaS applications they were logged into at 2 p.m. Those session tokens are valid until their natural expiration. If the session lifetime is 24 hours and the user logged in at 8 a.m., the session remains valid until 8 a.m. Saturday.
This is a bounded window, but it’s a real window. In the gap between deprovisioning and session expiration, a deprovisioned user with physical access to their old laptop (or someone else with that laptop) can continue using the SaaS applications.
The mitigation is to enforce session invalidation on identity change, either through short session lifetimes, or through real-time session revocation APIs that push the “this user is no longer valid” signal to the resource systems. Few SaaS platforms support the push model. Most rely on the token’s natural expiration.
For high-risk deprovisioning, a security concern, a contentious departure, the session token issue is worth attention. The laptop should be recovered, the browser sessions should be invalidated through platform admin APIs where possible, and the short-lived access tokens should be allowed to expire naturally before announcing the departure. For routine deprovisioning, the window is small enough that most companies accept the risk without active mitigation.
the inventory you don’t have
The OAuth grants, PATs, session tokens, and API keys across your environment constitute an inventory that almost no mid-market company has. The inventory is discoverable, every major SaaS platform exposes it through admin APIs, but it’s scattered across a dozen systems, each with its own format, and nobody has assembled it.
The consequences of not having the inventory are concrete. You don’t know how many tokens are outstanding. You don’t know which of them are tied to former employees. You don’t know how many are being actively used. You don’t know which applications are accumulating standing access through refresh tokens issued long ago.
Assembly is mechanical. For each of your top five SaaS platforms, export the OAuth grants table. Export the PATs list. Export the API keys. Correlate each entry with the identity that issued it. Flag anything tied to a deprovisioned identity.
The output is a list of tokens that should have been revoked at deprovisioning and weren’t. That list, in a mid-market company running this for the first time, typically has between fifty and several hundred entries.
Revoke them. Then fix the deprovisioning workflow so it revokes tokens at source, not just SSO credentials.
the test
You can run this test in under an hour if you have admin access to your primary SaaS platforms.
Pull a list of everyone whose SSO account was deprovisioned in the last six months. Call this list D.
For each person on D, query the OAuth grants table in Google Workspace (if applicable), Microsoft 365 (if applicable), Salesforce (if applicable), and GitHub (if applicable). For each platform, check: does this deprovisioned user have OAuth grants that are still active?
The answer will be yes for a meaningful fraction of them. The fraction depends on how well your offboarding workflow handles token revocation: small where someone wired revocation into offboarding, large where nobody did.
For each active grant you find, check the last-used timestamp. A grant that was last used recently, within the last 30 days, is a grant that is currently being exchanged for access tokens by some running application. That application has continuous access to your data, through a user who no longer works at your company.
The grant can be revoked in one admin click.
The logout cleared the cookie. The deprovisioning cleared the login. The tokens are still live. They will be live until someone explicitly revokes them. That someone has to be you.
access creep · every two weeks
One mechanism, one practical step.
The letter opens soon. Until then, everything is on this site: start with the offboarding checklist.
