Because a refresh token is long-lived and grants the ability to mint new access tokens indefinitely, its own theft is a serious, high-value compromise — which is why production systems don't just issue one and reuse it forever without additional protection.
Refresh token rotation: every time a refresh token is used to get a new access token, the authorization server also issues a new refresh token and invalidates the old one. This turns a stolen refresh token into a detectable event rather than a silent, permanent compromise:
Normal use:
Client uses refresh_token_1 --> gets access_token + refresh_token_2 (refresh_token_1 now dead)
Client uses refresh_token_2 --> gets access_token + refresh_token_3 (refresh_token_2 now dead)
Theft detected:
Attacker steals refresh_token_2 (already used by the legitimate client) and tries to use it
Server sees refresh_token_2 was already consumed --> treats this as reuse of a dead token,
a strong signal of theft --> revokes the ENTIRE token family, forcing the real user to log in again
That reuse-detection step is what makes rotation meaningfully better than a static, unrotated refresh token — a stolen-but-unused token is indistinguishable from a legitimate one until someone actually tries to use it, and rotation is what turns that first use into a tripwire.
Revocation more generally is the reason systems that need real-time "kill this session right now" behavior (a user reports a stolen device, an admin disables an account) can't rely on a JWT's exp field alone — a JWT already issued stays cryptographically valid until it naturally expires, no matter what happens to the account afterward. Real revocation requires either checking a server-side token blacklist/allowlist on every request (which reintroduces the per-request database or cache lookup that stateless JWTs were partly meant to avoid) or keeping access token lifetimes short enough that the exposure window after revocation is acceptably small, and treating actual instant revocation as the refresh token's job — revoke the refresh token, and the current access token simply expires naturally within minutes without being renewable.