Skip to content

Require PKCE and bind redirect_uri in lazy-auth-server token exchange#681

Merged
ochafik merged 2 commits into
modelcontextprotocol:mainfrom
mel-anthropic:mel/lazy-auth-pkce-hardening
Jun 2, 2026
Merged

Require PKCE and bind redirect_uri in lazy-auth-server token exchange#681
ochafik merged 2 commits into
modelcontextprotocol:mainfrom
mel-anthropic:mel/lazy-auth-pkce-hardening

Conversation

@mel-anthropic
Copy link
Copy Markdown
Contributor

@mel-anthropic mel-anthropic commented Jun 2, 2026

Summary

Follow-up to #679, hardening the example's mock authorization server:

  • PKCE is now mandatory: /authorize rejects requests without an S256 code_challenge. The MCP auth spec requires PKCE of clients; previously a code issued without a challenge skipped verification at the token endpoint entirely.
  • Single-use authorization codes: replaying a code at the token endpoint fails with invalid_grant (RFC 6749 §4.1.2); redeemed code IDs are tracked in memory until the code's own 5-minute expiry.
  • redirect_uri binding at /token: authorization-code exchanges that include a redirect_uri not matching the authorization request are rejected (RFC 6749 §4.1.3). OAuth 2.1 clients that omit it at the token endpoint still work, relying on the now-mandatory PKCE binding.

Test plan

  • npm run --workspace examples/lazy-auth-server build
  • Authorize without PKCE / with plain method → 400
  • Token exchange without code_verifierinvalid_grant
  • Auth-code replay (second exchange of the same code) → invalid_grant
  • Token exchange with mismatched redirect_uriinvalid_grant
  • Token exchange with verifier + matching redirect_uri → success
  • Token exchange with verifier and no redirect_uri (OAuth 2.1 style) → success
  • Hardened build re-served and connected from a remote MCP host (full OAuth click-through was verified e2e pre-hardening in Add lazy-auth-server example #679; the hardened paths are covered by the curl cases above, which include the S256 + omitted-redirect_uri flow real hosts use)

Hardens the example's mock authorization server:

- /authorize now requires a PKCE S256 code_challenge (the MCP auth
  spec mandates PKCE for clients; previously a code issued without a
  challenge skipped verification at the token endpoint)
- /token rejects authorization-code exchanges where a provided
  redirect_uri does not match the authorization request (RFC 6749
  §4.1.3); OAuth 2.1 clients that omit it still work, relying on the
  now-mandatory PKCE binding
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 2, 2026

Open in StackBlitz

@modelcontextprotocol/ext-apps

npm i https://pkg.pr.new/@modelcontextprotocol/ext-apps@681

@modelcontextprotocol/server-basic-preact

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-preact@681

@modelcontextprotocol/server-basic-react

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-react@681

@modelcontextprotocol/server-basic-solid

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-solid@681

@modelcontextprotocol/server-basic-svelte

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-svelte@681

@modelcontextprotocol/server-basic-vanillajs

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vanillajs@681

@modelcontextprotocol/server-basic-vue

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vue@681

@modelcontextprotocol/server-budget-allocator

npm i https://pkg.pr.new/@modelcontextprotocol/server-budget-allocator@681

@modelcontextprotocol/server-cohort-heatmap

npm i https://pkg.pr.new/@modelcontextprotocol/server-cohort-heatmap@681

@modelcontextprotocol/server-customer-segmentation

npm i https://pkg.pr.new/@modelcontextprotocol/server-customer-segmentation@681

@modelcontextprotocol/server-debug

npm i https://pkg.pr.new/@modelcontextprotocol/server-debug@681

@modelcontextprotocol/server-lazy-auth

npm i https://pkg.pr.new/@modelcontextprotocol/server-lazy-auth@681

@modelcontextprotocol/server-map

npm i https://pkg.pr.new/@modelcontextprotocol/server-map@681

@modelcontextprotocol/server-pdf

npm i https://pkg.pr.new/@modelcontextprotocol/server-pdf@681

@modelcontextprotocol/server-scenario-modeler

npm i https://pkg.pr.new/@modelcontextprotocol/server-scenario-modeler@681

@modelcontextprotocol/server-shadertoy

npm i https://pkg.pr.new/@modelcontextprotocol/server-shadertoy@681

@modelcontextprotocol/server-sheet-music

npm i https://pkg.pr.new/@modelcontextprotocol/server-sheet-music@681

@modelcontextprotocol/server-system-monitor

npm i https://pkg.pr.new/@modelcontextprotocol/server-system-monitor@681

@modelcontextprotocol/server-threejs

npm i https://pkg.pr.new/@modelcontextprotocol/server-threejs@681

@modelcontextprotocol/server-transcript

npm i https://pkg.pr.new/@modelcontextprotocol/server-transcript@681

@modelcontextprotocol/server-video-resource

npm i https://pkg.pr.new/@modelcontextprotocol/server-video-resource@681

@modelcontextprotocol/server-wiki-explorer

npm i https://pkg.pr.new/@modelcontextprotocol/server-wiki-explorer@681

commit: caf0020

Track redeemed code IDs (jti) in memory until the code's own 5-minute
expiry; replaying a code at the token endpoint now fails with
invalid_grant (RFC 6749 §4.1.2). Also documents why wildcard CORS is
intentional for this demo (browser-based hosts must read
WWW-Authenticate; no ambient credentials exist to protect).
@ochafik ochafik merged commit a990780 into modelcontextprotocol:main Jun 2, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants