How does PKCE (Proof Key for Code Exchange) work?
The client creates a random code verifier and sends only its hash, the code challenge, with the authorization request; when redeeming the code it sends the verifier itself, and the authorization server checks that hashing it gives the stored challenge.
* Only the client that created the secret verifier can redeem the code: the server compares SHA-256(verifier) with the challenge it stored. *
- Before the authorization request, the client generates a high-entropy random string, the code verifier (43 to 128 characters), and derives the code challenge from it:
code_challenge = BASE64URL(SHA-256(code_verifier)), signalled ascode_challenge_method=S256. - Authorization request: the client sends the code challenge (not the verifier). The authorization server stores it alongside the code it issues.
- The user authenticates and consents; the code comes back via the redirect as usual.
- Token request: the client sends the authorization code plus the code verifier.
- Validation: the server hashes the verifier and compares the result with the stored challenge. Only if they match does it issue the ID and access tokens.
The point is that the verifier never travels through the browser before the code is redeemed, and the hash cannot be reversed. Someone who intercepts the redirect has the code and maybe the challenge, but not the verifier, so they cannot redeem the code.
Tip: A common simplification says the challenge is "sent again" at the token endpoint. Precisely, the verifier is sent, and the server recomputes the challenge from it.
Go deeper:
RFC 7636: Proof Key for Code Exchange — how the verifier and challenge are created and checked.
oauth.com: Protecting Apps with PKCE — a worked explanation with example values.
oauth.net: PKCE — status, history and why PKCE is now recommended for all clients.