An authorization code should live for seconds to a few minutes, with a maximum of 10 minutes recommended by RFC 6749, and it must be redeemable only once. If a code is used twice, the server must reject the second attempt and should revoke the tokens already issued from it.
What the code is for
In the authorization code flow, the redirect back to your app carries a code, not a token. The app then sends the code to the token endpoint and receives tokens. The code is a short lived, one use ticket that travels through the browser, where it can show up in history, logs and referrer headers.
Because it passes through such exposed places, the spec limits both its lifetime and its reuse.
What RFC 6749 says
The authorization code must expire shortly after it is issued, to reduce the risk of leaks. A maximum lifetime of 10 minutes is recommended. The client must not use a code more than once. If a code is used more than once, the authorization server must deny the request and should revoke, when possible, all tokens previously issued from that code. The code is also bound to the client identifier and the redirect URI.
RFC 9700 adds a related point: a code injection attempt will also fail if the legitimate user already redeemed the code and it was one time use only.
Implementation checklist
| Property | What to do |
|---|---|
| Lifetime | Short, measured in seconds to a few minutes, never beyond 10 minutes |
| Randomness | Unguessable, from a cryptographically secure generator |
| Single use | Mark as used atomically at redemption |
| Binding | Store client ID, redirect URI and PKCE challenge with the code |
| Reuse response | Deny, and revoke tokens issued from that code |
| Logging | Log the reuse event, but not the code value |
Why atomic marking matters
If two requests present the same code at the same moment, a naive "check then mark" lets both succeed. Use a database transaction, a conditional update, or a cache operation that fails if the key was already consumed. Test it with concurrent requests, not just sequential ones.
What to do on reuse
Treat it as a possible theft signal. Reject the request with an error, revoke access and refresh tokens that came from that code, and consider ending the session. Record the client, time and source address so you can investigate.
A legitimate client can trigger reuse by retrying after a network timeout. That is one reason to return a clear error and let the user sign in again rather than silently accept the retry.
For client developers
- Send the code to the token endpoint once. If the call fails with a network error, restart the sign in flow rather than replaying blindly.
- Never log the full redirect URL, since it contains the code.
- Use PKCE so a stolen code is useless without your verifier.
- Strip the code from the address bar after handling, for example by replacing the history entry.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how an API scan reports authentication findings, then compare with the checklist above for your own token endpoint.
Quick check
Can I make codes last longer to avoid errors?
Longer codes raise the leak window. The spec recommends 10 minutes at most, and most flows complete in seconds.
Does PKCE replace single use?
No. They work together. PKCE ties the code to the client that asked, and single use limits replay.
Safeguard's SBOM and dependency scanning shows which OAuth server and client libraries you ship. There is a free scan.
Sources
- RFC 6749, The OAuth 2.0 Authorization Framework
- RFC 9700, Best Current Practice for OAuth 2.0 Security
Last reviewed: 19 October 2026.