Skip to content

Add OAuth 2.1 support for MCP server#14

Merged
rafalzawadzki merged 5 commits intomainfrom
rafalzawadzki/mcp-oauth
Mar 24, 2026
Merged

Add OAuth 2.1 support for MCP server#14
rafalzawadzki merged 5 commits intomainfrom
rafalzawadzki/mcp-oauth

Conversation

@rafalzawadzki
Copy link
Copy Markdown
Contributor

@rafalzawadzki rafalzawadzki commented Mar 24, 2026

Summary

  • Wraps Cloudflare Worker with @cloudflare/workers-oauth-provider to enable OAuth 2.1 authentication (DCR, PKCE, token management)
  • Users connecting via Claude Desktop/Claude.ai are redirected to the Supadata dashboard for login, which returns a signed JWT with their API key
  • Legacy API key headers (x-api-key, x-api-token, supadata-api-key) continue to work unchanged — fully backward compatible

New files

  • src/auth-handler.ts — Hono router handling /authorize (redirect to dashboard) and /callback (JWT validation + completeAuthorization)

Modified files

  • src/worker.ts — Wrapped with OAuthProvider, added PRM metadata endpoint, legacy API key bypass
  • wrangler.toml — Added KV namespace binding and routes for OAuth endpoints
  • package.json — Added @cloudflare/workers-oauth-provider, hono, jose, @cloudflare/workers-types
  • tsconfig.json — Added @cloudflare/workers-types

Worker secrets required

  • MCP_JWT_SECRET — shared HMAC secret for validating dashboard-signed JWTs
  • DASHBOARD_URLhttps://dash.supadata.ai
  • COOKIE_ENCRYPTION_KEY — for OAuth flow session cookies

Depends on

  • Dashboard PR (supadata-ai/chicago-v1) for the consent page at /oauth/mcp

Test plan

  • Verified /.well-known/oauth-protected-resource returns correct PRM metadata
  • Verified /.well-known/oauth-authorization-server returns correct AS metadata
  • Verified POST /register (DCR) successfully registers clients
  • Verified legacy API key auth (x-api-key header) still works
  • End-to-end OAuth flow via Claude Desktop after dashboard consent page is deployed

🤖 Generated with Claude Code

Wraps the Cloudflare Worker with @cloudflare/workers-oauth-provider to
enable OAuth 2.1 authentication (DCR, PKCE, token management) alongside
the existing API key auth. Users connecting via Claude Desktop or
Claude.ai are redirected to the Supadata dashboard for login, which
returns a signed JWT containing the user's API key. Legacy API key
headers (x-api-key, x-api-token, supadata-api-key) continue to work
unchanged.

- Add auth-handler.ts (Hono router for /authorize + /callback)
- Wrap worker.ts with OAuthProvider, serve PRM metadata
- Add KV namespace binding for OAuth token storage
- Add routes for OAuth endpoints (.well-known/*, /authorize, /token, etc.)
- Add @cloudflare/workers-oauth-provider, hono, jose dependencies

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 24, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
supadata-mcp 0717d88 Mar 24 2026, 09:51 PM

rafalzawadzki and others added 4 commits March 24, 2026 21:52
MCP clients (Claude Desktop, Claude.ai) need the resource_metadata URL
in the WWW-Authenticate header to discover the OAuth server (RFC 9728).
The library's default 401 response doesn't include it, so we use the
onError callback to add it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /authorize path was being intercepted by a conflicting route on
the api.supadata.ai zone (likely the API gateway). Moving all OAuth
endpoints under /oauth/ avoids any conflicts:
- /authorize → /oauth/authorize
- /token → /oauth/token
- /register → /oauth/register
- /callback → /oauth/callback

Also simplified wrangler routes to use a single /oauth/* wildcard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude sends resource=https://api.supadata.ai/ (with trailing slash) in
the authorize request. This gets stored as the token audience. When
validating the token on /mcp, the library computes the resource server
as protocol://host (no trailing slash) and does strict equality — which
fails. Strip trailing slashes from the resource parameter before storing
the grant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The root cause from worker logs:
1. Claude sends resource=https://api.supadata.ai/ (trailing slash) in
   both the authorize request and token exchange POST body
2. The library stores this as the token audience
3. When validating tokens on /mcp, the library computes the resource
   server as protocol://host (no trailing slash) and does strict equality
4. "https://api.supadata.ai" !== "https://api.supadata.ai/" → 401

Fix: strip the resource parameter from two places:
- auth-handler.ts: delete resource from the grant before completeAuthorization
- worker.ts: strip resource from the token exchange POST body before
  it reaches the OAuthProvider

This prevents any audience from being stored in tokens, skipping the
audience validation entirely on MCP requests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rafalzawadzki rafalzawadzki merged commit 9938938 into main Mar 24, 2026
2 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.

1 participant