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.
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 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.