Protocols and how-tos
PKCE explained: how it protects the authorization code
How Proof Key for Code Exchange works, why S256 and not plain, why confidential clients should use it too, and the errors that quietly disable it.
The short answer
PKCE (RFC 7636) binds an authorization code to the client that requested it. The client invents a random secret (the code verifier), sends only its SHA-256 hash (the code challenge) on the authorization request, and must present the original verifier when it exchanges the code for tokens. A code intercepted in transit is useless without the verifier, which never left the client.
Sources last reviewed .
The attack PKCE closes
In the authorization code flow, the code travels back to the client in a browser redirect. On mobile platforms another app can register the same custom URL scheme and receive it; in browsers it can leak through logs, referrer headers or a compromised redirect. Without PKCE, a public client has no secret to prove it is the party that started the flow, so whoever holds the code can redeem it.
PKCE adds a per-request secret that never travels through the browser. The server stores the challenge with the code and refuses the token request unless the verifier hashes to it.
The flow, step by step
Every step below is from RFC 7636 sections 4.1 to 4.6.
- Generate a code verifier: a high-entropy random string of 43 to 128 characters from the unreserved URL character set. Generate it fresh for every authorization request.
- Derive the code challenge: BASE64URL(SHA256(verifier)), without padding.
- Send code_challenge and code_challenge_method=S256 on the authorization request, alongside state (and nonce, for OpenID Connect).
- Keep the verifier where only this client can read it — server-side session for a web app, memory or secure storage for a native app.
- When the code comes back, send it with code_verifier to the token endpoint.
- The server hashes the verifier, compares it to the stored challenge, and issues tokens only on a match. Codes are single-use either way.
Why S256 and never plain
RFC 7636 permits a plain method where the challenge equals the verifier. It exists only for clients that cannot compute SHA-256, which in practice no longer describes anything. With plain, anyone who observes the authorization request has the verifier. The OAuth 2.0 Security BCP (RFC 9700) recommends S256, and a server should reject plain outright rather than accept it as a fallback.
Confidential clients should use it too
PKCE was originally written for public clients, which hold no secret. RFC 9700 now recommends it for confidential clients as well, because it also defends against authorization code injection — an attacker substituting a stolen code into a victim's session. A client secret proves who is redeeming the code; PKCE proves the redemption belongs to the same flow that started it. They answer different questions.
How implementations quietly lose the protection
- Reusing one verifier across requests, or deriving it from something predictable such as a session ID.
- Storing the verifier in a place the attacker controls — a query parameter, a non-HttpOnly cookie on a shared domain, or localStorage in an app with an XSS hole.
- A server that treats PKCE as optional when the parameters are absent. Downgrade by omission is the most common server-side failure; the server should require it per client.
- Accepting the plain method because a library defaulted to it.
In Authreads
How this applies to Authreads
The Authreads authorization endpoint requires PKCE with S256 on every authorization code request, and a non-empty nonce for OpenID Connect; plain is not accepted.
Authreads is a multi-tenant identity platform in beta. Capability status is maintained on /roadmap as Available, In progress or Planned.
Questions
Does PKCE replace the state parameter?
No. State protects the client against cross-site request forgery on its redirect endpoint and carries the client's own context. RFC 9700 notes PKCE can provide CSRF protection when the server enforces it, but state (or the OIDC nonce) remains the usual way a client correlates a response with its request.
Is PKCE needed for the client credentials grant?
No. PKCE protects the authorization code. The client credentials grant has no code and no browser redirect.
Keep reading
Building on any of this?
Tell us what you're putting together. We'll tell you plainly whether Authreads fits it.