Token families and refresh rotation
How refresh tokens are grouped into a family, why each one is used only once, and what identity does when a used token comes back.
On this page
A refresh token is a long-lived key that an application keeps so it can get new access tokens without sending you through sign-in again. That makes it valuable to a thief. Identity limits the damage with two ideas: tokens are used once, and tokens belong to a family.
What a family is#
When an application first receives a refresh token, identity opens a token family. A family stands for one grant: this application, for this person, from this sign-in, with these scopes. Every refresh token that descends from that first one belongs to the same family.
A family records:
- the person and the application,
- the session the sign-in created,
- the scopes granted,
- the key the tokens are bound to, if any, and
- a sliding expiry and an absolute expiry.
A family can only exist with a session. A grant with no person behind it, such as a service credential, never gets a refresh token.
Rotation: each use returns a new token#
Every time an application uses a refresh token, identity does two things at once. It issues a new refresh token, and it retires the one that was just used. The application must save the new one and throw the old one away.
- The application sends refresh token A.
- Identity returns a new access token and refresh token B, and marks A as used.
- Later the application sends B, and receives C, and so on.
Only the latest token in a family is ever valid. This is what makes theft detectable. If a thief copies a token, then either the thief or the real application will use it second, and that second use is a retired token coming back.
The short retry window#
Networks fail. An application may send token A, identity may rotate it, and the response may be lost on the way back. The application then retries with A, which has been retired.
For this case only, identity allows a retry window of a few seconds (10 seconds by default; an operator can shorten it). If a retired token comes back within that window, identity gives the same new token again instead of treating it as theft. After the window, a retired token is a reuse.
The window is short on purpose. Anything inside it is a token that can be replayed without the family being revoked, so the window is the exposure. A few seconds covers a dropped response and a client retry. It does not give a thief time.
What happens on reuse#
If a retired token is presented after the window, identity assumes it was stolen.
- The whole family is revoked. Nothing from that grant works any more.
- The session ends. The sign-in the family came from is revoked.
- The rest of the account is closed down as a precaution. In a follow-up step, every other session, refresh token family and personal access token of the account that existed before the reuse is ended, and applications that registered for back-channel logout are told.
- You are told. A security email says that a reused sign-in token ended your access as a precaution. This notice cannot be switched off.
The application that sent the token gets the ordinary error invalid_grant. It is the same answer as for any bad refresh token. This is deliberate: a different answer would tell a thief that the theft was noticed, and would tell a legitimate client nothing it could act on. The application's correct response is to send the person through sign-in again.
Other ways a family ends#
| Event | Effect on the family |
|---|---|
| The session ends: sign-out, idle limit, absolute limit or revocation | The family can no longer be refreshed. A refresh against an ended session is refused and the family is revoked. |
| You revoke the application under Connected apps | The application's families for your account are revoked. See Consent. |
| The family reaches its absolute expiry | It is refused. The absolute expiry never goes past the session's. |
| The application is suspended | Its families are revoked. |
| The session is tied to a single sign-on connection that does not reach the family's organization | A refresh is refused with invalid_grant. See Sessions. |
| The organization enforces single sign-on and the session has no recent one | A refresh is answered with insufficient_user_authentication and the person must sign in through the organization's connection again. Behind the flag sso_enforcement, OFF by default. |
The scopes were narrowed so offline_access is gone | A refresh is refused. |
Bound tokens and rotation#
Where the family is bound to a key with DPoP, a refresh must prove the same key. A request that proves a different key is refused with invalid_dpop_proof, and the token is not spent. Reuse detection runs before this check, so a stolen token presented under a thief's own key still counts as reuse.
A refresh that fails#
If a refresh fails after the old token was retired, for example because the request asked for a scope it was not granted, identity rolls the rotation back. The application keeps the token it had, so a mistake in one parameter cannot lose it its grant.
For people who build applications#
- Save the new refresh token from every refresh before you use the new access token.
- Refresh in one place at a time. Two parallel refreshes with the same token look like reuse once the window has passed.
- Retry a failed refresh promptly if the response never arrived. Do not retry after a long wait.
- Treat
invalid_grantas "sign the person in again". Do not retry it.
The developer steps for refreshing are in Log users in.