2026-09-09 · 6 min read · Rahul Chitturi
- oauth
- pkce
- authentication
An OAuth authorization request can succeed and still fail at the token endpoint because the code_verifier does not reproduce the original code_challenge. The defect is often in storage, encoding, redirect state, or application-instance routing rather than the authorization code itself.
PKCE binds one authorization request to one high-entropy verifier. Treat the verifier like a temporary credential: never place it in URLs, analytics, shared logs, or support screenshots.
Reconstruct the S256 transformation
For S256, hash the verifier's ASCII bytes with SHA-256, Base64url-encode the digest, and omit padding. Compare that result with the challenge sent on the authorization request using a synthetic fixture.
- Do not Base64-decode the verifier before hashing
- Use the URL-safe alphabet with hyphen and underscore
- Do not add line breaks, quotes, or Unicode normalization
- Send code_challenge_method=S256 explicitly
Trace verifier ownership
Associate the verifier with the browser transaction that initiated login, and consume it once when exchanging the returned code. A second tab, stale callback, serverless instance, or overwritten session entry can select another verifier.
Validate state separately from PKCE. State protects callback correlation and cross-site request forgery; PKCE proves possession of the verifier. One successful check does not compensate for a failed or omitted other check.
Reproduce with safe Flashman fixtures
Use the hash tool for a public verifier fixture, Base64 for alphabet checks, URL tool to inspect authorization parameters, and diff to locate transformed characters. Keep real codes, verifiers, client secrets, and tokens out of browser tools.
Test concurrent tabs, callback retries, application restarts, mixed redirect URIs, missing padding, lowercase parameter names, verifier length limits, and one-time authorization code handling.