OAuth 2.1
Spec-compliant OAuth for remote MCP clients via WorkOS AuthKit
Overview
The /mcp endpoint supports the MCP authorization spec (2025-11-25): this
server acts as an OAuth 2.1 resource server, and WorkOS
AuthKit acts as the authorization
server. AuthKit handles client registration (Client ID Metadata Documents
and Dynamic Client Registration), PKCE, and user consent, then issues access
tokens audience-bound to this server. Clients like Claude (web and Desktop)
connect with zero manual provisioning: no client IDs, no pasted keys.
API keys (X-API-KEY) and WorkOS session JWTs keep working as parallel auth
paths for machine and internal clients.
What the server implements
- Protected Resource Metadata (RFC 9728) at
/.well-known/oauth-protected-resource/mcp(path form, tried first by clients) and/.well-known/oauth-protected-resource. It names the canonical resource URI and points at your AuthKit instance. - Authorization Server Metadata (RFC 8414) at
/.well-known/oauth-authorization-server(and the/mcppath form). AuthKit is the authorization server and publishes its own RFC 8414 document; the spec-correct discovery path is PRM → AuthKit. But many MCP clients and registry crawlers probe this well-known against the resource server origin directly, so the server redirects (307) those requests to AuthKit's authoritative metadata. Both endpoints return404when OAuth is unconfigured. - Discovery hint on 401: unauthenticated
/mcprequests getWWW-Authenticate: Bearer realm="mcp", resource_metadata="...", which is how clients bootstrap the flow. - Audience-bound token validation (RFC 8707): AuthKit access tokens are
verified against the AuthKit JWKS, with the issuer pinned to your AuthKit
domain and the audience pinned to
MCP_PUBLIC_URL. Tokens minted for any other resource are rejected. The token'ssubbecomes theuser_idseen by tools (samecurrent_user()plumbing as the other auth methods), and ascopeclaim maps onto the server's scope checks.
Setup
- In the WorkOS dashboard, enable AuthKit and go to Connect →
Configuration. Enable Client ID Metadata Documents (and optionally
Dynamic Client Registration for older clients), and register your MCP
server's public URL as a resource indicator, e.g.
https://your-deployment.example.com/mcp. - Set the two environment variables in
.env:
WORKOS_AUTHKIT_DOMAIN=https://your-env.authkit.app
MCP_PUBLIC_URL=https://your-deployment.example.com/mcpMCP_PUBLIC_URL must match the resource indicator exactly (no trailing
slash); tokens are audience-bound to this string, so a mismatch yields silent
401s.
- Restart
mymcp-serve. Verify discovery:
curl https://your-deployment.example.com/.well-known/oauth-protected-resource/mcp{
"resource": "https://your-deployment.example.com/mcp",
"authorization_servers": ["https://your-env.authkit.app"],
"bearer_methods_supported": ["header"]
}- Add the server to an MCP client by URL alone (no headers). The client discovers AuthKit, registers itself, and opens the consent screen.
Conformance testing
MCPJam ships an OAuth conformance suite. It drives a real browser consent flow, so it is a manual check rather than part of CI:
npx -y @mcpjam/cli@latest oauth conformance \
--url https://your-deployment.example.com/mcp \
--protocol-version 2025-11-25 \
--conformance-checksProtocol and apps conformance run in CI against the API-key path (see
make mcp_conformance).