PKCE Explained
Proof Key for Code Exchange (PKCE) hardens the authorization code flow for public clients. It binds the token request to the original authorisation request using a one-time secret the client proves it knows.
What you'll learn
- Explain the code-interception attack PKCE prevents
- Describe the verifier and challenge relationship
- Walk through how PKCE augments the code flow
- Know why PKCE is now recommended for all clients
6 min
The attack PKCE stops
Public clients such as mobile and single-page apps cannot safely store a client secret, because anything shipped to a device or browser can ultimately be extracted by a determined user or attacker. Without a secret, an attacker who intercepts the authorisation code — for instance through a malicious app registered for the same custom URL scheme on the device — could exchange that code for tokens. PKCE closes this hole by adding a dynamically generated secret to each individual request.
Because the secret is created fresh for every authorisation attempt and is never stored anywhere persistent, there is nothing long-lived for an attacker to steal ahead of time. Intercepting the code alone no longer suffices. This is precisely why PKCE is mandated for public clients on the Merion API, and why modern guidance treats it as the default rather than an advanced extra.
Verifier and challenge
The client generates a random, high-entropy code verifier and from it derives a code challenge, typically by hashing the verifier with SHA-256 and base64url-encoding the result. It sends only the challenge on the initial authorisation request, keeping the verifier entirely private on the client side until the very end.
code_challenge = base64url(sha256(verifier))When the client later exchanges the code for tokens, it presents the original verifier. The authorisation server recomputes the challenge from that verifier and rejects the exchange unless the result matches the challenge it stored earlier. This proves the same client that started the flow is the one finishing it, since only that client ever knew the verifier. The hash is one-way, so seeing the challenge tells an attacker nothing useful about the verifier.
How it fits the code flow
PKCE does not replace the authorization code flow; it augments it with two small additions and changes nothing else about the shape of the exchange. Every step is identical to the standard flow except that a code challenge travels outbound on the authorisation request, and the matching verifier travels back during the token exchange. No client secret is required at any point, which is exactly what makes the technique suitable for public clients that have nowhere safe to keep one.
Because the additions are so contained, adopting PKCE is rarely disruptive: most of your existing flow code stays the same. If you have not yet read the base flow it builds on, start with The Authorization Code Flow, then layer this binding on top of that understanding.
Recommended everywhere
Although PKCE was originally designed for public clients, current guidance recommends applying it to confidential clients too, not just the ones that lack a secret. The extra binding costs almost nothing in code or performance, and it provides genuine defence in depth against authorisation-code injection attacks, so there is little reason to omit it from any new integration.
In practice, most modern OAuth libraries enable PKCE by default, and you may already be using it without having configured it explicitly. It is still worth confirming your client does, and that it prefers the SHA-256 challenge method over the weaker plain method whenever the platform can perform hashing. Treating PKCE as standard equipment, rather than a special case for mobile, is the simplest way to stay aligned with current best practice.
Key takeaways
- PKCE prevents stolen authorisation codes from being redeemed
- A per-request verifier and its derived challenge bind the exchange
- It augments the code flow without needing a client secret
- Current guidance recommends PKCE for confidential clients as well
FAQ
Is PKCE only for mobile apps?
No. It originated there but is now recommended for all client types, including server-side apps, as defence in depth against code interception.
Which challenge method should I use?
Prefer S256, which hashes the verifier with SHA-256. The plain method sends the verifier unhashed and should only be used if hashing is genuinely unavailable.
Does PKCE replace the client secret?
For public clients it removes the need for one. Confidential clients can use both a secret and PKCE together for layered protection.
Ready to build?
Read the API reference, grab the OpenAPI spec, and ship a resilient integration.