What is PKCE?
PKCE (pronounced “pixie”) is a security extension to OAuth 2.0 that prevents authorization code interception attacks. It’s mandatory in OAuth 2.1.
The Problem
Without PKCE, an attacker who intercepts an authorization code can exchange it for tokens:
The Solution
PKCE adds cryptographic proof that the same client completing the flow started it:
How It Works
Generate Code Verifier
Client generates a random 32-byte string:const verifier = base64url(crypto.randomBytes(32));
// Example: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
Create Code Challenge
Hash the verifier with SHA-256:const challenge = base64url(sha256(verifier));
// Example: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
Send Challenge
Include challenge in authorization request:/oauth2/authorize?
code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
code_challenge_method=S256
Store Verifier
Client stores verifier securely (e.g., sessionStorage)
Send Verifier
Include verifier when exchanging code:POST /oauth2/token
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
Server Verifies
Server computes challenge from verifier and compares:const computed = base64url(sha256(received_verifier));
if (computed === stored_challenge) {
// ✓ PKCE verified!
}
Implementation
Using Auth-Agent SDK
The SDK handles PKCE automatically:
import { AuthAgentClient } from "ai-auth";
const client = new AuthAgentClient({
clientId: "your_agent_id",
redirectUri: "http://localhost:3000/callback",
});
// PKCE is generated and verified automatically
await client.redirectToLogin();
Manual Implementation
// 1. Generate verifier
const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
// 2. Create challenge
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest("SHA-256", data);
const challenge = base64url(new Uint8Array(hash));
// 3. Store verifier
sessionStorage.setItem("pkce_verifier", verifier);
// 4. Build auth URL
const authUrl =
`https://api.auth-agent.com/oauth2/authorize?` +
`client_id=your_id&` +
`redirect_uri=http://localhost:3000/callback&` +
`response_type=code&` +
`code_challenge=${challenge}&` +
`code_challenge_method=S256`;
// 5. Later, when exchanging code:
const storedVerifier = sessionStorage.getItem("pkce_verifier");
// Send storedVerifier with token request
Security Benefits
Prevents Code Interception
Even if an attacker intercepts the authorization code, they cannot use it without the verifier.
Unlike client secrets, the verifier is never shared or registered in advance.
Effective in browsers, mobile apps, and SPAs where secrets can’t be kept.
OAuth 2.1 makes PKCE mandatory for all authorization code flows.
Common Issues
Verifier Not Found: Make sure you’re storing the verifier before
redirecting and retrieving it correctly after callback.
PKCE Verification Failed: The verifier must be the exact same value that
generated the challenge. Check for encoding issues.
References
Next Steps