Articles

Refresh Token Rotation Explained: Safer Long Sessions

Refresh token rotation issues a new refresh token on every use and revokes the old one, so a stolen token gets caught on reuse. How it works and pitfalls.

Chisato Chisato · · 5 min read
A padlock resting on a computer keyboard under red light

Refresh token rotation is a technique where every time a client uses its refresh token to get a new access token, the authorization server also issues a brand-new refresh token and invalidates the old one. Each refresh token works exactly once. If an attacker steals one and uses it, either the attacker or the legitimate client will eventually present a token that has already been used, and the server treats that reuse as evidence of theft and shuts down the whole session. It lets applications keep users signed in for long periods without leaving a long-lived, reusable credential lying around.

Why refresh tokens exist

Modern authentication usually splits credentials in two:

  • Access tokens are short-lived, often minutes, and are sent with every API request. They’re frequently JWTs, validated by signature without a database lookup.
  • Refresh tokens are longer-lived and are only ever sent to the authorization server’s token endpoint, to obtain a new access token when the current one expires.

The split limits damage. If an access token leaks, it stops working soon. But it moves the risk onto the refresh token: a stolen refresh token can mint fresh access tokens for as long as it remains valid, potentially days or weeks. Without rotation, the server has no way to tell the legitimate client and a thief apart, because both present the same valid token.

How rotation works

With rotation enabled, the refresh flow looks like this:

  1. The client sends refresh token R1 to the token endpoint.
  2. The server validates R1, marks it as used, and returns a new access token plus a new refresh token R2.
  3. Next time, the client uses R2, receives R3, and so on.

All tokens descending from the original login form a token family. The server keeps track of which token in the family is currently valid.

The security payoff comes from reuse detection. Suppose an attacker steals R2:

  • If the attacker uses R2 first, they receive R3. When the real client later tries R2, the server sees a token that’s already been used.
  • If the real client uses R2 first, the attacker’s later attempt with R2 is the reuse.

Either way, the server sees an already-redeemed token presented again. It can’t know which party is legitimate, so it does the safe thing: revoke the entire token family. Both parties lose access, and the real user has to sign in again. The theft is contained, and the event can be logged and alerted on.

Static vs rotating refresh tokens

Static refresh tokenRotating refresh token
ReuseCan be used many timesSingle use
Theft detectionNoneReuse revokes the family
Exposure windowFull token lifetimeUntil next legitimate refresh
Server stateToken record or revocation listToken family and current token
Client complexityLowMust store each new token reliably
Best forConfidential server-side clientsPublic clients: SPAs, mobile, desktop

Confidential clients, such as a backend that authenticates to the authorization server with its own secret, already have strong protection because a stolen refresh token alone isn’t enough to redeem it. Rotation matters most for public clients, which can’t keep a secret. That’s why it’s commonly recommended alongside PKCE for single-page apps and native apps.

Absolute and idle lifetimes

Rotation alone could keep a session alive forever, since each new token resets the clock. Pair it with two limits:

  • Idle timeout — if no refresh happens within a window, the family expires. Abandoned sessions die on their own.
  • Absolute lifetime — no matter how active the session is, the family expires after a fixed maximum, forcing a fresh login.

These bounds are a policy decision based on the sensitivity of the application. A banking app and a recipe site shouldn’t share defaults.

The race-condition problem

The main operational headache with rotation is legitimate concurrency. Picture a single-page app open in two tabs, or a mobile app that fires several API calls at once right as the access token expires. Two requests try to refresh simultaneously with the same token. The first succeeds; the second looks exactly like reuse, and the whole family gets revoked. The user is logged out for no reason.

Common mitigations:

  • Serialize refreshes on the client. Keep a single in-flight refresh promise and have every caller await it, rather than starting their own. In browsers, the Web Locks API can coordinate this across tabs.
  • Grace period on the server. Allow the just-replaced token to be redeemed again for a few seconds, returning the same successor rather than revoking. This trades a sliver of security for robustness.
  • Handle network failures. If the server rotated the token but the response never reached the client, the client still holds the old one. A short grace period also covers this case.

Where to store refresh tokens

Rotation reduces the impact of theft; it doesn’t make storage unimportant. Guidance by platform:

  • Browser apps: prefer keeping refresh tokens out of JavaScript entirely, for example with a backend-for-frontend that holds tokens server-side and gives the browser a session cookie with the HttpOnly, Secure, and SameSite attributes. If the token must live in the browser, rotation becomes essential, since cross-site scripting can read anything JavaScript can.
  • Mobile and desktop: use the platform’s secure storage, such as the OS keychain or keystore.

For an even stronger guarantee, DPoP binds tokens to a key pair held by the client, so a stolen token is useless without the private key. Rotation and sender-constraining work well together.

Implementing it

Most identity providers offer rotation as a configuration setting, usually with reuse detection and a configurable grace window. If you’re building your own authorization server, the core requirements are:

  • Store a hash of each refresh token, never the raw value.
  • Record the family ID, the token’s parent, and whether it’s been used.
  • Make the “check unused, mark used, issue successor” step atomic, so two simultaneous requests can’t both succeed.
  • On reuse outside any grace period, revoke every token in the family and log a security event.

To inspect what’s inside the access tokens your provider issues, paste one into the JWT decoder. Refresh tokens are often opaque random strings, deliberately carrying no readable claims.

The takeaway

Refresh token rotation makes every refresh token single-use: each redemption returns a new one and retires the old. Presenting a retired token signals theft, and the server revokes the whole token family. Use it for public clients like SPAs and mobile apps, bound it with idle and absolute lifetimes, serialize client-side refreshes or allow a short server grace period to avoid false logouts, and keep the tokens themselves in the most protected storage your platform offers.

Chisato Chisato · · 5 min read

What Is an Open Redirect? Risks and How to Prevent It

An open redirect lets attackers use your domain to send users to any URL. How open redirect vulnerabilities work, why they matter, and how to fix them.

#Security #Web Development #Authentication
Chisato Chisato · · 4 min read

What Is a Man-in-the-Browser Attack?

A man-in-the-browser attack uses malware inside the browser itself to alter what a user sees and submits, bypassing HTTPS and session protections entirely.

#Security #Authentication #Web Development