OAuth PKCE adds a one-time proof to the authorization-code flow. The client creates a secret verifier before sending the browser to the authorization server, sends only a derived challenge in the authorization request, and later proves possession of the verifier when it exchanges the returned code.
PKCE stands for Proof Key for Code Exchange. It is not a password replacement, an encryption layer for access tokens or a way to skip redirect-URI checks. It binds one authorization request to the client instance that started it.
Standards checked: RFC 7636, RFC 9700 and RFC 10017 were reviewed on 27 October 2026. This explainer describes the S256 method recommended by current OAuth security guidance.
The problem PKCE addresses
In an authorization-code flow, the browser carries an authorization code from the authorization server back to the client. Without an extra proof, a party that intercepts that code may try to redeem it first.
This risk is especially visible for public clients, which cannot keep a shared client secret safely. Installed applications and browser-based applications distribute code to devices or browsers controlled by users. A static secret embedded in that code is not a meaningful secret.
PKCE makes the intercepted code insufficient on its own. The token endpoint also expects the unpredictable verifier created for that particular authorization request.
The four moving parts
| Value | Created by | Sent where | Purpose |
|---|---|---|---|
| code_verifier | Client | Token request | One-time high-entropy proof |
| code_challenge | Client | Authorization request | Derived value bound to the verifier |
| code_challenge_method | Client | Authorization request | Says how the challenge was derived |
| authorization code | Authorization server | Redirect to client | Short-lived code later exchanged for tokens |
RFC 7636 requires a verifier between 43 and 128 characters from the permitted unreserved character set. It must be generated with enough entropy to resist guessing.
For S256, the challenge is the base64url encoding of the SHA-256 digest of the verifier, without padding. The verifier itself does not go through the browser in the authorization request.
The flow step by step
- The client generates a fresh random code verifier.
- It derives the S256 code challenge.
- The client sends the authorization request with the challenge and method.
- The authorization server authenticates the user and obtains authorization.
- The server redirects back with an authorization code.
- The client sends the code and original verifier to the token endpoint.
- The server derives the challenge from the submitted verifier and compares it with the value attached to the authorization request.
- Tokens are issued only when the values match and the other OAuth checks pass.
A new flow needs a new verifier. Reusing a verifier across requests weakens the binding and makes incident analysis harder.
Why S256 matters
RFC 7636 defines a plain method in which the challenge equals the verifier. That method does not protect against an attacker who can observe the authorization request.
RFC 9700 says clients should use a challenge method that does not expose the verifier in the authorization request. S256 is the interoperable method for that purpose.
If a client is capable of using S256, it should not fall back to plain because a server rejects S256. That response suggests the server does not meet the security profile the client expected.
PKCE is now broader than mobile apps
PKCE was designed around interception risks for public native clients. Current OAuth security guidance applies it more widely.
RFC 9700 says public clients must use PKCE and recommends it for confidential clients because it also helps against authorization-code injection. RFC 10017 requires public browser-based clients to implement PKCE and authorization servers to support and enforce it for those clients.
A confidential web application still authenticates at the token endpoint when its deployment supports that. PKCE adds request binding; it does not replace client authentication.
What the authorization server must remember
The server associates the submitted challenge and method with the authorization request. When it later receives the code, verifier and client information at the token endpoint, it verifies:
- the code is valid and unused;
- the code belongs to the expected client;
- the redirect URI matches the authorization request when required;
- the verifier transforms into the stored challenge;
- the request satisfies client-authentication rules where applicable.
The code should be short-lived and single-use. PKCE does not make long-lived or reusable authorization codes safe.
PKCE and the state parameter solve different problems
PKCE binds the code exchange to the client instance that started the flow. The state parameter can bind the redirect response to the browser’s initiating session and carry protection against cross-site request forgery when used correctly.
One does not automatically replace the other. Current designs may also use OpenID Connect nonce values, issuer checks and exact redirect-URI matching. Each check covers a narrower boundary.
Redirect URIs still matter
A weak redirect configuration can send the authorization response somewhere unintended. Exact registered redirect URIs reduce that risk.
Native applications may use claimed HTTPS redirects, loopback redirects or custom schemes under the rules of their platform profile. Browser-based applications should follow their current best-practice profile. PKCE does not make an open redirect harmless.
If a client accepts any path, wildcard or untrusted subdomain, an attacker may gain a route to the code before the verifier check is reached. Even when redemption fails, leaked codes and confusing redirects remain an incident.
What PKCE does not protect
Stolen access tokens. PKCE protects the code exchange, not a token after issuance. Token storage, sender constraints, scope and lifetime need separate controls.
A compromised client. Malware running inside the client context may see the verifier or tokens.
Phishing. A correctly executed OAuth flow can still be presented from a malicious application or deceptive interface.
Overbroad scopes. PKCE does not reduce the permissions the user approves.
Bad redirect validation. The authorization server must still validate redirect URIs and client identity.
The user’s browser session. Session security, cookies and authentication remain separate.
PKCE also says nothing about how the client itself was built or whether a release artifact came from the expected pipeline. That is a separate assurance problem; our SLSA build provenance guide explains what evidence a build system can attach to an artifact.
Implementation mistakes to look for
A verifier that is too short or predictable
A timestamp, UUID with insufficient entropy or a fixed string is not an adequate verifier. Use a cryptographic random-number generator and the permitted encoding.
Reusing a verifier
Generate one per authorization request and delete it after success or expiry.
Logging the verifier
Application logs, analytics events and crash reports can turn the proof into another exposed credential. Redact it along with codes and tokens.
Losing the verifier across a redirect
A browser client may need to keep the verifier briefly while the user leaves and returns. Store it only for the transaction and bind it to the initiating session. Do not place it in a URL.
Accepting a downgrade
Do not silently change from S256 to plain. Treat unsupported S256 as a compatibility decision requiring explicit review.
Checking PKCE but not the rest of OAuth
Verify issuer, redirect URI, state or equivalent session binding, client identity and token response. PKCE is one control in a chain.
A practical review checklist
- Generate a fresh, unpredictable verifier for every request.
- Use S256.
- Send only the challenge in the authorization request.
- Keep the verifier out of URLs, logs and analytics.
- Bind the verifier to the initiating browser or app transaction.
- Enforce PKCE at the authorization server for public clients.
- Keep authorization codes short-lived and single-use.
- Validate the redirect URI exactly.
- Retain state or another appropriate session-binding control.
- Test simultaneous sign-in attempts so verifiers cannot be mixed.
- Fail closed when a verifier is missing or does not match.
For software that supports more than one identity provider, test each provider separately. A library option called “PKCE enabled” does not prove that every provider enforces the challenge.





