invalid_grant security auth_error ai_generated true

OAuth2 refresh token replay detected: same refresh token used more than once

ID: security/oauth2-token-replay-via-refresh-token

Also available as: JSON · Markdown · 中文
80%Fix Rate
85%Confidence
1Evidence
2023-05-15First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
OAuth 2.0 RFC 6749 active
Spring Security OAuth2 2.5.x active
Keycloak 21.0.x active
Auth0 Node.js SDK 2.0.0 active
Okta .NET SDK 3.0.0 active

Root Cause

OAuth2 refresh token is not rotated after each use, allowing an attacker who steals the token to reuse it indefinitely and obtain new access tokens.

generic

中文

OAuth2 刷新令牌在每次使用后未轮换,导致窃取令牌的攻击者可以无限重放以获取新的访问令牌。

Official Documentation

https://datatracker.ietf.org/doc/html/rfc6749#section-1.5

Workarounds

  1. 90% success Implement refresh token rotation: issue a new refresh token with each access token refresh and invalidate the old one. Example: In Spring Security OAuth2, set `refreshTokenRotationEnabled=true` in authorization server config.
    Implement refresh token rotation: issue a new refresh token with each access token refresh and invalidate the old one. Example: In Spring Security OAuth2, set `refreshTokenRotationEnabled=true` in authorization server config.
  2. 85% success Use refresh token expiration with a short TTL (e.g., 15 minutes) combined with rotation, and require re-authentication after expiry.
    Use refresh token expiration with a short TTL (e.g., 15 minutes) combined with rotation, and require re-authentication after expiry.
  3. 75% success Add a client-side nonce or binding to refresh token to tie it to a specific device or session, preventing cross-device replay.
    Add a client-side nonce or binding to refresh token to tie it to a specific device or session, preventing cross-device replay.

中文步骤

  1. Implement refresh token rotation: issue a new refresh token with each access token refresh and invalidate the old one. Example: In Spring Security OAuth2, set `refreshTokenRotationEnabled=true` in authorization server config.
  2. Use refresh token expiration with a short TTL (e.g., 15 minutes) combined with rotation, and require re-authentication after expiry.
  3. Add a client-side nonce or binding to refresh token to tie it to a specific device or session, preventing cross-device replay.

Dead Ends

Common approaches that don't work:

  1. 95% fail

    Longer expiration gives attackers more time to exploit stolen tokens; does not address the fundamental replay vulnerability.

  2. 70% fail

    Blacklists are hard to maintain in distributed systems and require real-time synchronization; attackers can still replay before blacklisting.

  3. 85% fail

    Rate limiting may block legitimate users and does not prevent a single replay attack; attacker can still use the token once before rate limit kicks in.