2 min read

OAuth Token Rotation Hardening

Account takeover response method through refresh token reuse detection and session invalidation policy

OAuth Token Rotation Hardening thumbnail

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.

OAuth Token Rotation 하드닝 커버
Wikimedia Commons 기반 무료 이미지

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

perspectiveDesign criteriaVerification points
rotationRe-issue refresh token for each requestConcurrent Request Conflict Rate
reuse detectionjti/nonce based detectionSuspicious Session Detection Time
invalidationfamily unit revokeForced logout reflection time
thanksSecurity event loggingAccident 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

Mermaid diagram rendering...

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.

Comments