Refresh-Token Rotation Is a Replay Detector

Sep 6

Refresh-token rotation is often described as “issue a new token and invalidate the old one.” Its security value comes from the history retained between them.

If both a legitimate client and an attacker possess one refresh token, one will eventually present a token that has already been consumed. The authorization server detects replay and revokes the active token family. Rotation converts otherwise invisible credential theft into an observable conflict.

Store a token family, not isolated strings

Persist only a cryptographic hash of each opaque token. Associate tokens with one authorization grant and a generation:

CREATE TABLE refresh_token (
  token_hash bytea PRIMARY KEY,
  family_id uuid NOT NULL,
  generation integer NOT NULL,
  client_id text NOT NULL,
  subject_id text NOT NULL,
  status text NOT NULL,
  expires_at timestamptz NOT NULL,
  consumed_at timestamptz,
  replaced_by_hash bytea,
  UNIQUE (family_id, generation)
);

The family lets the server revoke the currently active descendant when an ancestor is replayed. The client binding, scope, and resource-server audience must remain constrained to the original grant.

Rotation must be atomic

Two browser tabs or mobile requests can refresh concurrently. A naive read-then-write flow may allow both to observe the same active token and mint two children.

Use one transaction and lock the presented token row:

BEGIN
  load token FOR UPDATE
  verify client, expiry, grant, status
  if ACTIVE:
    mark CONSUMED
    insert generation + 1
    link replacement
    COMMIT and return new pair
  if CONSUMED:
    revoke family
    COMMIT and reject

The access token and replacement refresh token should be derived only after all policy checks. If the transaction fails, do not return credentials the database did not record.

Handle lost responses deliberately

The authorization server can commit rotation while the response is lost. The legitimate client still holds the consumed parent and retries; strict reuse detection then revokes its own family.

There are three defensible approaches:

PolicyBenefitCost
strict one-time usestrongest immediate replay signallost response forces login
very short retry grace returning same childtolerates network retryrequires safely recoverable response/token material
sender-constrained tokensstolen value alone is insufficientkey management and client support

RFC 9700 requires public clients receiving refresh tokens to use rotation or sender-constraining. DPoP and mutual TLS are standardized ways to bind tokens to key possession in applicable deployments.

A grace window must not mint multiple children. It should recognize the same client instance and return the already committed outcome, or deliberately accept the reauthentication cost.

Security response

On confirmed reuse, revoke the active family, emit a security event, invalidate relevant server-side sessions where policy requires it, and require a new authorization grant. Do not attempt to guess whether the first or second presenter was the attacker.

Revoke on password change, logout, client compromise, or other high-confidence security events according to product policy. Apply inactivity and maximum lifetimes so continuous rotation does not create an immortal credential.

Observability without leakage

Log family and generation identifiers, client ID, policy outcome, and coarse risk signals. Never log raw tokens. Monitor reuse rate, concurrent refresh conflicts, family revocations, refresh latency, and login recovery rate. Sudden reuse spikes may indicate a client concurrency bug rather than a new attacker campaign; the response must still preserve security.

Common mistakes

  • Storing refresh tokens in plaintext.
  • Invalidating the parent without retaining family history.
  • Performing rotation outside one transaction.
  • Extending the absolute grant lifetime on every refresh.
  • Putting bearer tokens in URLs or analytics logs.
  • Treating a device label as cryptographic sender constraint.

Trade-offs

Strict rotation detects replay but introduces a distributed race between client persistence and server commit. Retry grace improves availability while expanding state and replay analysis. Sender-constrained tokens reduce the value of theft, but require protected client keys.

Rotation is not token housekeeping. It is a replay-detection protocol whose failure behavior must be designed before the happy path.

Further reading

>