Topic guide · 3 min read
OAuth downgrade attacks
Short answer
An OAuth downgrade attack makes a client or authorization server use a weaker option than intended, such as dropping PKCE, switching the code_challenge_method to plain, falling back to the implicit flow, or confusing which server issued a response; servers stop them by enforcing PKCE and allowed flows per client, and clients by never offering weaker fallbacks and checking the iss parameter.
Key takeaways
- RFC 9700 requires authorization servers to reject a code_verifier when the authorization request had no code_challenge.
- Register each client's allowed grant types and response types; reject anything else.
- Clients that use several authorization servers should check the iss response parameter (RFC 9207) to stop mix-up attacks.
Security protocols tend to accumulate options for compatibility, and every option is a possible downgrade. OAuth 2.0 has several: grant types, response types, PKCE methods and the choice of server itself. An attacker who can influence which option is used can sometimes steer a flow to the weakest one.
Which downgrades matter in OAuth?
PKCE removal, PKCE method downgrade, flow downgrade to implicit, and mix-up between authorization servers.
| Attack | What the attacker changes | Defense |
|---|---|---|
| PKCE downgrade | Strips code_challenge from the request |
Server rejects code_verifier if no challenge was sent; or requires PKCE |
| Method downgrade | code_challenge_method=plain |
Server allows only S256; client never sends plain |
| Flow downgrade | response_type=token instead of code |
Server restricts response types per client |
| Mix-up | Makes the client send a code to the wrong server | Client checks iss in the response (RFC 9207); distinct redirect URIs per server |
| Client auth downgrade | Weaker client authentication method | Server registers one method per client |
How does PKCE downgrade work?
If a server accepts both PKCE and non-PKCE requests for a client, an attacker can obtain a code without a challenge and then try to inject it into a victim's flow.
RFC 9700 section 4.8 describes the attack and the fix: if there was no code_challenge in the authorization request, the server must reject a token request that contains a code_verifier. Servers that simply require PKCE for every authorization code request get this protection automatically. The OAuth 2.1 draft requires PKCE throughout.
How do mix-up attacks work?
A client that talks to several authorization servers is tricked into sending a code or token from one server to another, controlled by the attacker.
The defense from RFC 9207 is small: the authorization server includes its iss identifier in the redirect, and the client checks it equals the server it started with. Clients can also use a distinct redirect URI per server. Single-server clients are less exposed but lose nothing by checking.
What should the authorization server enforce?
Per-client registration of everything that could be downgraded.
- Allowed grant types and response types.
- PKCE required, S256 only, for all clients if possible.
- One client authentication method per client.
- Exact redirect URIs.
issreturned in authorization responses, and advertised in metadata.
What should the client do?
Offer only the strong option, fail closed, and validate the response.
- Always send S256 PKCE; never fall back to plain or to no PKCE on error.
- Never fall back to implicit.
- Check
stateandissbefore redeeming the code. See the state parameter in OAuth 2.0.
Paste your authorization request into the Authorization URL Builder; it flags implicit response types, missing PKCE and plain challenges. The OAuth security guide covers the wider threat list.
Questions
What is a PKCE downgrade attack?
An attacker removes the code_challenge from an authorization request, so the code is issued without PKCE, then redeems it in a way the server fails to tie back to the original client. RFC 9700 section 4.8 describes it and requires servers to mitigate it.
Is code_challenge_method=plain a downgrade?
It weakens PKCE because the verifier itself travels through the browser. RFC 7636 allows plain only when the client cannot do SHA-256. Servers may refuse plain, and many do.