mcp

PKCE for MCP OAuth: what the code_verifier does and why clients must send it

PKCE is how an MCP client proves that the authorization code it's redeeming is the one it asked for. The MCP authorization spec (2025-06-18) says "MCP clients MUST implement PKCE according to OAuth 2.1." The client creates a secret code_verifier and a code_challenge derived from it. The challenge goes out with the authorization request. The verifier stays with the client until the token request, where the client sends it. In the spec's words, this ensures "that only the original requestor can exchange an authorization code for tokens." If sign-in works but the token exchange fails, check the verifier before you rewrite anything else.

Who this is for

You're building an MCP client or a remote MCP server with OAuth, and you want to know what PKCE does, where each value goes, and how to tell a PKCE problem from everything else.

Where each value travels

StepWhat the client sendsWhat the spec says
1. Before sign-inNothing yet. The client generates the PKCE parametersThe spec's sequence diagram starts with "Generate PKCE parameters"
2. Authorization requestAuthorization URL + `code_challenge` + `resource`The browser opens the authorization URL with the challenge
3. Token requestAuthorization code + `code_verifier` + `resource`The authorization server checks the verifier against the earlier challenge
4. Calling the MCP serverAccess tokenThe MCP server, as a resource server, validates the token

The pairing matters because, as the spec puts it, PKCE "helps prevent authorization code interception and injection attacks by requiring clients to create a secret verifier-challenge pair." An attacker who intercepts the code doesn't have the verifier, so they can't redeem it. For the `resource` parameter that sits next to both values, see token audience and resource mismatch.

Pitfalls

  • Generating a new verifier per request. The verifier sent with the token request has to be the one that produced the challenge for that same authorization request. If you regenerate it in between, nothing will match.
  • Losing the verifier across a redirect. If the client restarts or loses state between the browser sign-in and the token request, the original verifier is gone.
  • Treating PKCE as client authentication. PKCE protects one code exchange. It doesn't stand in for client registration. For when you need registration at all, see when you need DCR.
  • Checking PKCE in the wrong place. The authorization server checks the verifier. An MCP server that only acts as a resource server validates the access token instead.

How to check it

  1. Log the `code_challenge` on the outgoing authorization URL.
  2. Log the `code_verifier` on the token request and confirm it's the value that produced that challenge, from the same sign-in attempt.
  3. Confirm both requests carry the same `resource` value.
  4. If the token request still fails, read the authorization server's error response before changing client code.
  5. Once a token comes back, debug the MCP server call separately. For the full auth sequence, see the MCP auth checklist and Protected Resource Metadata in plain English.

Where ShipKits fits

If you only need to understand PKCE, this post and the free templates at /free are enough. When you want a first desk day, the Day-1 Desk is a $12 pay-once first desk day: two seats (Day Desk + Reviewer Lite), one path-lock skill, a thin gate card, install.sh, and a ~90-minute sample run. Cheat Sheet for the skim checklist mindset.

Soft next step

Add the four log lines from "How to check it" to your client today and run one sign-in. Free starter files are at /free. When you want a first desk with a path-lock skill, the Day-1 Desk ($12) has it.

FAQ

What is PKCE in MCP OAuth?+

A check that ties an authorization code to the client that asked for it. The client creates a secret verifier and a challenge derived from it, so only the original requestor can exchange the code for tokens.

Is PKCE required for MCP clients?+

Yes. The MCP authorization spec says MCP clients must implement PKCE according to OAuth 2.1.

What is the difference between code_verifier and code_challenge?+

The code_challenge goes out with the authorization request. The code_verifier stays secret until the token request, where the client sends it so the authorization server can match it to the earlier challenge.

Does PKCE replace a client secret?+

No. PKCE protects one authorization code exchange from interception or injection. It doesn't identify the client the way a client secret or other client authentication does.

What suggests a PKCE mismatch?+

The browser sign-in succeeds but the token request is rejected. Check that the token request sends the same code_verifier that produced the challenge for that authorization request.

Do MCP servers have to check PKCE themselves?+

Not usually. The verifier is checked when the code is exchanged for tokens, which is the authorization server's job. An MCP server acting only as a resource server validates the access token it receives.

Get the free starter

Value first, then a kit when you are ready. No fake urgency. Start with Harness Desk Starter at /free.