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.

PKCE

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.

Try It — OAuth 2.0 Flow Visualizer

Open full tool