DEV Community

yutianle
yutianle

Posted on

Refresh Token Rotation Without Breaking Every Session

Refresh Token Rotation Without Breaking Every Session

A refresh token is valuable because it survives the short lifetime of an access token. If an attacker copies one, the client can keep obtaining new access tokens until the authorization server expires or revokes the underlying grant. OAuth 2.0 Security Best Current Practice, RFC 9700, addresses that risk for public clients by requiring either a sender-constrained refresh token or refresh token rotation.

Rotation sounds simple: return a new refresh token with every successful refresh and invalidate the old one. Production behavior is harder. A mobile app may retry a request after a lost response. Several tabs may share a cookie store. A background worker may refresh while the foreground application does the same. If the server treats every use of the old token as an attack, ordinary races can revoke real users. If it accepts old tokens for too long, stolen-token detection becomes weak.

Bind every token to a grant family

The authorization server needs durable state that connects each refresh token to a grant family, client, subject, issue time, expiry, and status. The token itself should be unpredictable and stored only as a hash where possible. The family record should include the current token identifier, revoked status, and enough metadata to investigate a replay without retaining raw credentials.

On refresh, the server validates the presented token, checks that it is the active token for its family, and atomically marks it consumed while issuing its successor. The atomic step matters. Two parallel requests must not both treat the same token as active. A transactional compare-and-set operation, a database uniqueness rule, or an equivalent coordination mechanism can enforce that condition.

The response should bind the new token to the same authorization context unless a deliberate downscoping operation changes it. Do not move a token to another user, client, or tenant during rotation. Recalculate expiry from policy and make revocation of the family revoke associated access tokens or sessions where the service architecture allows it.

Treat reuse as a grant-level signal

RFC 9700 describes reuse detection as part of refresh token rotation. When a consumed token appears again, assume that at least two parties possess refresh material. Revoke the token family rather than only rejecting that request. An attacker who copied the predecessor should lose access, and the legitimate client should reauthenticate.

This rule needs a controlled exception for operational uncertainty. If the server sent a rotated token and the connection failed before the client received it, the client has the old token while the server has recorded it as consumed. The next request looks like replay. A short idempotency mechanism can return the same successor for the exact same authenticated refresh request, but it must be narrowly scoped. A general grace window that accepts every old token creates a larger replay window and complicates revocation.

Client behavior should reduce races as well. Serialize refresh operations within a process, share the fresh result across tabs, and avoid starting a second refresh after the response status is unknown. On a replay-related error, clear the local grant and send the user through authentication. Do not loop with the old token.

Make migration and recovery observable

Existing clients may not understand a changed refresh response. Deploy rotation with a client inventory and a rollout plan. Log the client identifier, grant family, token generation, outcome, and reason code, but never log the token value. Track reuse detections, family revocations, failed refresh bursts, and reauthentication volume. A sudden rise in family revocation may indicate a client concurrency bug, while repeated reuse from changing networks may warrant investigation.

Provide an administrative revocation path for a user or grant. Recovery should revoke the affected family and require a new authorization flow. A support process that silently issues another long-lived token to bypass detection defeats the control.

Finally, rotation is one layer. RFC 9700 also recommends sender-constrained tokens such as mutual TLS or DPoP when appropriate. Short access-token lifetimes, redirect URI validation, proof key for code exchange, and secure browser storage remain relevant. Rotation gives the authorization server a chance to notice copied refresh material. It does not repair a client that leaks every new token.

Test the failure modes before release: simultaneous refreshes, a response lost after commit, token replay, revocation during an active request, client clock drift, database failover, and restoration from backup. Verify that a reused token invalidates the family, the legitimate path can reauthenticate, and no old token remains usable after the documented recovery window.

References

Top comments (0)