Reading — step 1 of 5
Read
Refresh-Token Rotation with Reuse Detection
Short access tokens (15 min) + long refresh tokens (days) is the dominant pattern. But long-lived refresh tokens can be stolen — laptop seized, browser extension compromised, log file leaked. Mitigation: rotation with reuse detection (OAuth 2.1 draft, Auth0, Okta).
The rules
-
ISSUE: server hands the client a pair
(access, refresh)in a new family (think: a chain ID). -
REFRESH: client presents
refresh_n. Server returns(access_{n+1}, refresh_{n+1})in the same family and marksrefresh_nas USED. -
If
refresh_nis presented a second time (i.e. its status is USED), that means someone already rotated past it. Either:- The user replayed by accident — harmless.
- An attacker stole the refresh token, used it, and now the legitimate client is hitting the stale copy. Or vice versa.
Either way, the server cannot tell — but it knows the family is compromised. Revoke the entire family. All access and refresh tokens issued in this chain are dead.
Why this works
Without rotation, a stolen refresh is useable indefinitely. With rotation
- reuse detection, the attacker has at most until the legitimate client makes its next refresh — then the alarm fires, the user is logged out, and a session re-auth (password, MFA) re-establishes trust.
Refresh tokens are NOT JWTs (usually)
Refresh tokens are opaque random strings stored server-side. Why?
- Revocation. JWTs are stateless; revocation requires a blacklist anyway, so why not store the token directly?
- Refresh tokens get used rarely (once per access-token lifetime), so the DB hit is fine.
Some shops still use JWTs for refresh — fine, but add a jti and check it
against the database every time, defeating the "no DB lookup" benefit of
JWT.
What you'll build
A toy issuer/refresher with:
ISSUE user→ new familyREFRESH rt_id→ rotate or detect reuseUSE_ACCESS at_id→ check current validity
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…