PKCE (Proof Key for Code Exchange) is the OAuth2 flow for public clients — mobile apps, SPAs, anything where you can't safely store a client_secret. It replaces the secret with a per-request cryptographic challenge that's impossible to intercept without breaking TLS.

Advertisement

Why public clients need PKCE

Traditional OAuth2 Authorization Code flow requires a client_secret on the token-exchange step. A mobile app can't keep secrets — anyone with the binary can extract them. Without protection, an attacker who intercepts the auth code (e.g., via custom URL scheme hijacking) can exchange it for tokens.

The PKCE handshake

1. Client generates random code_verifier (43-128 chars)
2. Client computes code_challenge = base64url(SHA256(code_verifier))
3. Client → AuthServer: /authorize?code_challenge=&method=S256
4. User authenticates; AuthServer stores code_challenge, returns auth_code
5. Client → AuthServer: /token with auth_code + code_verifier (NOT challenge)
6. AuthServer verifies SHA256(verifier) == stored challenge → issues tokens
Advertisement

Why it's secure

An attacker who steals only the auth_code can't exchange it without the code_verifier. The verifier never leaves the original device — only the (one-way hashed) challenge does. Even if the attacker intercepts the redirect URL, they don't have the verifier.

PKCE everywhere now

OAuth 2.1 (current draft) makes PKCE mandatory for ALL flows, including confidential clients. AWS Cognito, Auth0, Okta, Microsoft Entra all require or recommend it. If your app is doing OAuth in 2026 without PKCE, you're using a deprecated pattern.

Implementation gotcha

The code_verifier must use S256 (SHA-256) method, not 'plain' — 'plain' provides no protection and is only in the spec for legacy compatibility. Always use S256. Library defaults are usually correct; verify by inspecting the actual /authorize URL.

PKCE = SHA256(verifier) sent first, verifier sent at exchange. Mandatory for mobile + SPAs. Use S256 not plain.