flashman
← All posts

OAuth PKCE code verifier mismatch debugging

Debug PKCE token exchange failures by preserving the verifier, reproducing the S256 challenge, checking Base64url rules, and tracing redirects safely.

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.

Try these tools