2026-09-09 · 8 min read
- oauth
- pkce
- authentication
Proof Key for Code Exchange binds an OAuth authorization code to a secret created by the client for one authorization transaction. It protects a stolen code from being redeemed by a party that does not possess the original verifier.
PKCE complements redirect URI validation, state correlation, client authentication where applicable, and token validation. It does not turn an unsafe redirect or untrusted authorization server into a secure flow.
Generate an interoperable verifier
Create the verifier with a cryptographically secure random generator and encode it using only the unreserved characters allowed by the PKCE specification. Keep enough entropy even if the textual representation has the maximum permitted length.
- Use a maintained OAuth library when available.
- Prefer S256 and reject an unexpected downgrade to plain.
- Do not derive the verifier from a user password or timestamp.
- Never reuse one verifier across authorization requests.
Compute the challenge exactly
For S256, SHA-256 hashes the ASCII verifier bytes. Base64url then encodes the 32-byte digest using hyphen and underscore instead of plus and slash, with trailing equals padding omitted.
Do not hash a decoded Base64 value, a quoted string, a normalized Unicode string, or the challenge itself. Compare against published test vectors before diagnosing an authorization server.
Bind storage to one transaction
Store the verifier in a protected, short-lived transaction record associated with state and the exact callback flow. Consume it once when exchanging the code, then remove it whether the exchange succeeds or reaches a terminal failure.
- Support concurrent tabs without overwriting another verifier.
- Share transaction storage safely across application instances.
- Keep verifiers out of URLs, logs, analytics, and error pages.
- Expire abandoned records and bound their total count.
Test the complete browser round trip
Use Flashman's hash and Base64 tools with public PKCE fixtures, URL tool for redacted authorization parameters, diff for transformation evidence, and password generator only to explore safe random-character sets. Use production libraries for the actual protocol.
Test concurrent attempts, cancellation, back navigation, duplicate callbacks, stale state, process restarts, verifier boundaries, wrong redirect URIs, authorization-code replay, token endpoint errors, and browser privacy restrictions.