PKCE (Proof Key for Code Exchange) is an OAuth extension that ties an authorization code to the client that asked for it. The client creates a secret and sends a hash of it first, then proves it holds the secret when redeeming the code. The MCP specification requires clients to use PKCE with the S256 method.
Definitions
Authorization code. A short-lived value the authorization server sends back to the client after the user approves access. The client swaps it for an access token.
Code verifier. A random secret the client generates and keeps.
Code challenge. A hash of the verifier, sent with the first request. With S256 the hash is SHA-256.
Interception. An attacker obtains the authorization code, for example from a redirect they can observe on the device. Without PKCE, the code alone can be redeemed.
How the exchange works
- The client generates a verifier and computes the challenge.
- It sends the user to the authorization server with the challenge, a state value, and the MCP server it wants a token for.
- The user approves. The server redirects back with a code.
- The client calls the token endpoint with the code and the original verifier.
- The server hashes the verifier and compares it with the stored challenge. If they match, it issues the token.
An attacker who copies the code in step 3 has no verifier, so step 4 fails for them.
What the MCP specification asks
| Requirement | Detail |
|---|---|
| Clients must implement PKCE | Per OAuth 2.1 |
| Method | S256 when technically capable |
| Check support first | The client must look for code_challenge_methods_supported in the authorization server metadata |
| If the field is absent | The client must refuse to proceed |
| Redirect URIs | Registered and matched exactly |
| State | Clients should send and verify it, and discard mismatches |
The refusal rule matters. A client that quietly falls back to a flow without PKCE has removed the protection the specification relies on.
What to test in your own setup
- Start an authorization request without a challenge and confirm the authorization server rejects it.
- Check the metadata document for code_challenge_methods_supported and confirm S256 is listed.
- Confirm redirect URIs are exact strings, not patterns.
- Confirm access tokens are short-lived and refresh tokens for public clients rotate. The specification says so.
Common confusions
PKCE does not authenticate the user and does not replace token audience checks. It protects one step: the swap of code for token. You still need audience validation, minimal scopes and HTTPS everywhere.
Quick check
Is PKCE only for mobile apps?
No. It began as protection for public clients and is now a baseline for OAuth 2.1, including the clients MCP assumes.
What if my authorization server does not advertise PKCE support?
The MCP specification says the client must refuse to proceed. Ask the provider to enable and advertise S256.
Does PKCE stop a malicious MCP server?
No. It protects the code exchange. Malicious servers are handled by consent prompts, URL validation and sandboxing.
Where Safeguard fits
Safeguard scans the OAuth and MCP client libraries in your projects for known vulnerable versions and can gate pull requests that introduce them. Try a free scan at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.