Generating PKCE Code Verifier and Challenge
Step-by-step guide to generating a cryptographically secure PKCE code_verifier and code_challenge using the Web Crypto API in JavaScript.
Detailed Explanation
PKCE Code Verifier & Challenge Generation
The PKCE extension requires two values: a code_verifier (a random secret) and a code_challenge (a derived hash of the verifier). The verifier is kept by the client, while the challenge is sent to the authorization server during the authorization request.
Requirements (RFC 7636)
| Parameter | Requirements |
|---|---|
code_verifier |
43-128 characters from [A-Z] [a-z] [0-9] - . _ ~ |
code_challenge |
BASE64URL(SHA256(code_verifier)) when method is S256 |
code_challenge_method |
S256 (recommended) or plain (not recommended) |
JavaScript Implementation (Web Crypto API)
// Step 1: Generate a random code_verifier
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64url(array);
}
// Step 2: Derive the code_challenge
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await crypto.subtle.digest("SHA-256", data);
return base64url(new Uint8Array(digest));
}
// Helper: Base64URL encoding (no padding)
function base64url(buffer) {
let str = "";
for (const byte of buffer) {
str += String.fromCharCode(byte);
}
return btoa(str)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
// Usage
const verifier = generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier);
console.log("code_verifier:", verifier);
console.log("code_challenge:", challenge);
Python Implementation
import os
import hashlib
import base64
code_verifier = base64.urlsafe_b64encode(os.urandom(32)).rstrip(b"=").decode()
code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode()).digest()
).rstrip(b"=").decode()
Why S256 Instead of Plain?
With plain, the code_challenge equals the code_verifier, meaning an attacker who intercepts the challenge (sent in the front-channel redirect) immediately knows the verifier. With S256, the challenge is a SHA-256 hash — an attacker cannot reverse it to obtain the verifier. Always use S256.
Use Case
Generating PKCE values in a JavaScript SPA, React Native mobile app, or Node.js CLI tool before initiating the OAuth 2.0 authorization flow. The code_verifier is stored locally (e.g., sessionStorage) and the code_challenge is sent in the authorization request.