OAuth Token Rotation Hardening
Account takeover response method through refresh token reuse detection and session invalidation policy

Introduction
OAuth token rotation is not a recommendation, but an essential security measure for long-term services. If the refresh token is used as a fixed key, the scope of damage during theft increases and detection of abnormalities becomes difficult. This article summarizes hardening strategies including rotation, reuse detection, and session invalidation policies.

Problem definition
A system in which token rotation is only formally implemented is very difficult to respond to in the event of a security incident.
- There is no refresh token reuse detection, so stolen tokens are used for a long period of time.
- Chain invalidation is not possible because the token family state is not saved.
- When logging out, only the access token is discarded and the refresh token remains.
From a security perspective, the key is managing token families rather than single tokens. If a single reuse is detected, the entire associated session must be blocked.
Key concepts
| perspective | Design criteria | Verification points |
|---|---|---|
| rotation | Re-issue refresh token for each request | Concurrent Request Conflict Rate |
| reuse detection | jti/nonce based detection | Suspicious Session Detection Time |
| invalidation | family unit revoke | Forced logout reflection time |
| thanks | Security event logging | Accident investigation time required |
Token security does not end with the single function of the authentication server. To be effective, the API gateway, session storage, and notification system must be designed together.
Code Example 1: Token Family Model
export type RefreshTokenFamily = {
familyId: string;
userId: string;
currentJti: string;
revokedAt?: string;
lastSeenIp?: string;
};
export async function rotateRefreshToken(family: RefreshTokenFamily, nextJti: string) {
if (family.revokedAt) throw new Error("family revoked");
await tokenStore.updateFamily(family.familyId, {
currentJti: nextJti,
});
return signRefreshToken({ familyId: family.familyId, jti: nextJti });
}
Code example 2: Reuse detection logic
export async function verifyRefreshToken(payload: { familyId: string; jti: string }) {
const family = await tokenStore.findFamily(payload.familyId);
if (!family) throw new Error("invalid family");
if (family.currentJti !== payload.jti) {
await tokenStore.revokeFamily(payload.familyId, "reuse detected");
throw new Error("refresh token reuse detected");
}
return family;
}
Architecture flow
Tradeoffs
- Strengthening the rotation logic improves security, but increases token storage load.
- Reuse detection is powerful, but if concurrency processing is insufficient, even normal users may be logged out.
- Family invalidation is safe, but session maintainability may be reduced from a UX perspective.
Cleanup
The core of OAuth hardening is managing refresh tokens as stateful assets. Combining rotation and reuse detection and applying family-level invalidation can substantially reduce the risk of account takeover.
Image source
- Cover: source link
- License: CC BY-SA 4.0 / Author: Nhatem3
- Note: After downloading the free license image from Wikimedia Commons, it was optimized to JPG at 1600px.