invalid_grant security auth_error ai_generated true

OAuth2 authorization code reuse detected: same authorization code used more than once

ID: security/oauth2-authorization-code-reuse-detection

Also available as: JSON · Markdown · 中文
90%Fix Rate
88%Confidence
1Evidence
2023-11-20First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
Spring Security OAuth2 2.5.0 active
Keycloak 21.0.0 active
Okta 2024.02.0 active

Root Cause

The authorization server fails to properly mark authorization codes as consumed after first use, allowing an attacker to replay a previously intercepted code to obtain a new access token.

generic

中文

授权服务器未能正确标记授权码在首次使用后已消耗,允许攻击者重放之前截获的授权码以获取新的访问令牌。

Official Documentation

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

Workarounds

  1. 95% success Ensure the authorization server marks the code as consumed atomically upon first use. For example, in a database-backed implementation, use a transaction: UPDATE auth_codes SET consumed=true WHERE code=? AND consumed=false. If affected rows is 0, reject the request.
    Ensure the authorization server marks the code as consumed atomically upon first use. For example, in a database-backed implementation, use a transaction: UPDATE auth_codes SET consumed=true WHERE code=? AND consumed=false. If affected rows is 0, reject the request.
  2. 90% success Implement a one-time use token pattern: after issuing the access token, immediately invalidate the authorization code from the store (e.g., Redis: DEL auth_code:<code>).
    Implement a one-time use token pattern: after issuing the access token, immediately invalidate the authorization code from the store (e.g., Redis: DEL auth_code:<code>).
  3. 85% success Add a nonce or state parameter that must be unique per authorization request and validated during token exchange, preventing replay even if the code is intercepted.
    Add a nonce or state parameter that must be unique per authorization request and validated during token exchange, preventing replay even if the code is intercepted.

中文步骤

  1. Ensure the authorization server marks the code as consumed atomically upon first use. For example, in a database-backed implementation, use a transaction: UPDATE auth_codes SET consumed=true WHERE code=? AND consumed=false. If affected rows is 0, reject the request.
  2. Implement a one-time use token pattern: after issuing the access token, immediately invalidate the authorization code from the store (e.g., Redis: DEL auth_code:<code>).
  3. Add a nonce or state parameter that must be unique per authorization request and validated during token exchange, preventing replay even if the code is intercepted.

Dead Ends

Common approaches that don't work:

  1. 80% fail

    Longer expiration times actually increase the window for replay attacks, making the problem worse.

  2. 50% fail

    Authorization codes are randomly generated and not meant to be guessed; the issue is replay, not brute force.

  3. 70% fail

    Manual review is not scalable and cannot prevent real-time attacks; automated detection must be implemented.