OAuth 2.0 Refresh Token Rotation: Diagnosing False Logout Due to Reuse Detection
0 reputation · 12 Feb 2021, 07:05 UTC
0 reputation · 12 Feb 2021, 07:05 UTC
Clients occasionally experience abrupt logouts after a refresh token has been used once and then re‑used by a retry or race condition. The server treats the second use as a potential theft and revokes the entire token family, leaving users unexpectedly signed out.
The core question is whether reuse detection should trigger an automatic family revocation (fail‑closed) or serve merely as a signal for additional verification steps (fail‑open). Each path trades off user experience against security assurance.
Implementation‑specific behavior varies across authorization servers; some impose a grace window, others revoke immediately. Network latency, concurrent refresh calls, and device‑wide token sharing can all trigger false positives.
35275 reputation · 12 Feb 2021, 16:39 UTC
The abrupt logout is most likely a false positive from refresh token rotation reuse detection: a retry or concurrent request presented an already-rotated token, and the server treated it as theft. Whether the server should fail-closed and revoke the entire token family, or fail-open and require additional verification, is a policy decision. The immediate fix is to eliminate the client-side race, then tune server policy only if a grace window is available.
Likely: Two or more refresh calls raced. The first succeeded and rotated the token; the second, or a retried timeout, presented the old token. The server's reuse detector fired.
Confirmed: Refresh token rotation invalidates the presented token and issues a replacement. Reuse detection exists to catch replay of stolen tokens. Strict implementations revoke the whole token family on reuse to terminate a potentially compromised session.
Assumption: This guidance assumes a standard OAuth 2.0 authorization server with rotation enabled. Exact semantics vary by provider.
Serialize refresh calls. Use a single-flight mutex or promise queue so only one refresh runs at a time. Retry only after the in-flight request settles, and only on network errors—not on 4xx responses.
Inspect server logs. Look for invalid_grant or reuse-detection events correlated with the logout timestamps. Scoped example:
grep -i "invalid_grant\|reuse" /var/log/auth-server.log | grep "2026-10-15"
Reproduce the race. Fire two concurrent refreshes with the same token and observe whether the second triggers revocation. Scoped curl example:
curl -s -X POST https://auth.example.com/oauth/token \
-d grant_type=refresh_token \
-d refresh_token="$OLD_TOKEN" \
-d client_id="$CLIENT_ID" & \
curl -s -X POST https://auth.example.com/oauth/token \
-d grant_type=refresh_token \
-d refresh_token="$OLD_TOKEN" \
-d client_id="$CLIENT_ID" & wait
Check whether the second response is invalid_grant and whether the first token's family is revoked.
Check for a grace window. Some providers offer a short leeway where a just-used token is accepted once. Verify your server's documentation for a configurable reuse_grace_period or similar setting. Do not assume it exists.
Fail-closed revocation is the safer default because it terminates a potentially compromised session. Fail-open—issuing a new token but flagging the event—risks letting an attacker persist if step-up verification is weak. If your server supports a short grace period, it can reduce false logouts without materially widening the theft window. If it does not, client-side serialization is the only reliable fix.
Which authorization server and version are you running? Grace-period support and reuse-detection semantics differ across providers, so this detail determines whether server-side tuning is an option.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.