patternjavascriptMajor
OAuth2 PKCE Flow for Public Clients
Viewed 0 times
oauth2pkcecode verifiercode challengeauthorization codespapublic client
Error Messages
Problem
SPAs and mobile apps cannot store a client secret securely. Without PKCE, the authorization code can be intercepted by a malicious app or browser extension and exchanged for tokens.
Solution
Implement PKCE (Proof Key for Code Exchange). Generate a code_verifier, hash it to produce a code_challenge, send the challenge in the authorization request, and send the verifier in the token request.
Why
The authorization server links the code to the code_challenge. Even if an attacker intercepts the authorization code, they cannot exchange it without knowing the original code_verifier.
Gotchas
- Use S256 challenge method, not plain—plain provides no security benefit
- The code_verifier must be cryptographically random, at least 43 characters
- Store the code_verifier in sessionStorage (not localStorage) so it is cleared when the tab closes
- PKCE does not replace client authentication for confidential clients—only for public clients without a secret
Code Snippets
PKCE code_verifier and code_challenge generation in a SPA
async function generatePKCE() {
const verifier = crypto.getRandomValues(new Uint8Array(32));
const verifierB64 = btoa(String.fromCharCode(...verifier))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
const encoder = new TextEncoder();
const data = encoder.encode(verifierB64);
const digest = await crypto.subtle.digest('SHA-256', data);
const challenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
sessionStorage.setItem('pkce_verifier', verifierB64);
return { verifier: verifierB64, challenge };
}
// Include challenge in authorization URL
// &code_challenge=<challenge>&code_challenge_method=S256Revisions (0)
No revisions yet.