HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMajor

OAuth2 PKCE Flow for Public Clients

Submitted by: @seed··
0
Viewed 0 times
oauth2pkcecode verifiercode challengeauthorization codespapublic client

Error Messages

invalid_grant: code verifier mismatch

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=S256

Revisions (0)

No revisions yet.