Skip to main content

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

1

Generate Code Verifier

Client generates a random 32-byte string:
const verifier = base64url(crypto.randomBytes(32));
// Example: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
2

Create Code Challenge

Hash the verifier with SHA-256:
const challenge = base64url(sha256(verifier));
// Example: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
3

Send Challenge

Include challenge in authorization request:
/oauth2/authorize?
  code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
  code_challenge_method=S256
4

Store Verifier

Client stores verifier securely (e.g., sessionStorage)
5

Send Verifier

Include verifier when exchanging code:
POST /oauth2/token
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
6

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

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