PKCE stops stolen authorization codes from being redeemed, and is required for every client today.
PKCE (Proof Key for Code Exchange, RFC 7636, pronounced "pixy") protects the authorization code. If an attacker intercepts the code (a malicious app registered for the same custom URL scheme, a leaked log, a proxy), they could otherwise redeem it.
How it works:
- Before redirecting, the client generates a random code verifier: 43 to 128 characters of high entropy.
- It sends a code challenge in the authorize request:
BASE64URL(SHA256(verifier))withcode_challenge_method=S256. - The AS stores the challenge with the code.
- At the token endpoint the client sends the original
code_verifier; the AS hashes it and compares.
A thief who has the code but not the verifier cannot exchange it. The verifier never leaves the client until the back-channel token request.
PKCE was invented for public clients (mobile and single-page apps that cannot keep a secret), but RFC 9700 recommends it for all clients, including confidential server-side apps, because it also defends against authorization code injection. Always use S256; the plain method exists only for constrained legacy clients.
Store the verifier per login attempt (in the session or in memory) next to state and the OIDC nonce.
Example
import { randomBytes, createHash } from "node:crypto";
// 1. Per login attempt: random verifier (43-128 chars)
const verifier = randomBytes(32).toString("base64url");
// 2. Challenge = BASE64URL(SHA256(verifier))
const challenge = createHash("sha256").update(verifier).digest("base64url");
console.log(verifier.length); // 43
console.log(challenge.length); // 43
// authorize URL gets: code_challenge=<challenge>&code_challenge_method=S256
// token request gets: code_verifier=<verifier>
// RFC 7636 test vector:
const v = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
console.log(createHash("sha256").update(v).digest("base64url"));
// E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cMRemember
- Challenge = BASE64URL(SHA256(verifier)), method S256
- A stolen code is useless without the verifier
- Use PKCE for every client, public or confidential
Watch out for
- Reusing one verifier across logins or storing it where other sites can read it.