OAuth PKCE Error โ Fix
Updated April 2026
OAuth Error Response
{"error": "invalid_grant", "error_description": "PKCE verification failed: code_verifier does not match code_challenge"}The code_verifier you sent in the token exchange does not match the code_challenge you sent in the authorization request. They must be cryptographically linked โ the challenge is the SHA-256 hash of the verifier.
Common causes
- Verifier not stored before the redirect โ regenerated on the callback page
- Wrong base64 encoding โ must be base64url (no padding, - and _ instead of + and /)
- Wrong hash method โ must be SHA-256 (S256), not plain
- Verifier stored in localStorage but cleared (private browsing, storage policies)
Correct PKCE implementation
// Generate verifier and store BEFORE redirecting
const verifier = generateVerifier();
sessionStorage.setItem('pkce_verifier', verifier); // store here
const challenge = await sha256Base64url(verifier);
// redirect to auth with code_challenge=challenge
// On callback โ retrieve the STORED verifier
const verifier = sessionStorage.getItem('pkce_verifier'); // NOT regenerated
await exchangeCode(code, verifier);
function generateVerifier() { const array = new Uint8Array(32); crypto.getRandomValues(array); return btoa(String.fromCharCode(...array)) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
async function sha256Base64url(str) { const data = new TextEncoder().encode(str); const digest = await crypto.subtle.digest('SHA-256', data); return btoa(String.fromCharCode(...new Uint8Array(digest))) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
} Debug your OAuth PKCE error โ OAuthFixer