Skip to content

How It Works

AuthPlane is a standalone OAuth 2.1 authorization server purpose-built for the Model Context Protocol (Authorization spec 2025-11-25). It sits between MCP clients and your MCP server, handling the entire authorization dance so your server only needs to verify JWTs.

MCP CLIENTSClaude CodeClaude DesktopMCP InspectorAUTHPLANE AUTH SERVEROAuth 2.1 + PKCECIMD + DCR clientsConsent screenToken signing (ES256)JWKS endpointRefresh rotationYOUR MCP SERVERJWT verify onlyOffline validationvia cached JWKSoptionalYOUR IDPOkta · Entra · Google · Auth0

Key design principle: AuthPlane handles the OAuth complexity. Your MCP server verifies JWTs against a cached JWKS — once the keys are fetched, token validation happens locally.

When an MCP client connects to your server and gets a 401, it discovers which authorization server to use via RFC 9728 Protected Resource Metadata — a document the AuthPlane SDK serves from your MCP server:

GET /.well-known/oauth-protected-resource/mcp HTTP/1.1
Host: mcp.yourcompany.com
200 OK
{
"resource": "https://mcp.yourcompany.com/mcp",
"authorization_servers": ["https://auth.yourcompany.com"]
}

The client then fetches the AS metadata (RFC 8414) to discover all endpoints:

GET /.well-known/oauth-authorization-server HTTP/1.1
Host: auth.yourcompany.com
200 OK
{
"issuer": "https://auth.yourcompany.com",
"authorization_endpoint": "https://auth.yourcompany.com/oauth/authorize",
"token_endpoint": "https://auth.yourcompany.com/oauth/token",
"registration_endpoint": "https://auth.yourcompany.com/oauth/register",
...
}

Step 2: Client identification (CIMD or DCR)

Section titled “Step 2: Client identification (CIMD or DCR)”

Modern MCP clients identify themselves with a Client ID Metadata Document (CIMD, IETF draft) — a JSON document hosted at a URL the client controls. The client_id is the URL; AuthPlane fetches and validates the metadata (HTTPS required by default, SSRF-guarded):

{
"client_id": "https://client.example.com/.well-known/oauth-client.json",
"client_name": "Example MCP Client",
"redirect_uris": ["http://localhost:9876/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"]
}

The client generates a PKCE verifier/challenge pair (S256 — plain is rejected) and sends the user to the consent screen, binding the request to your MCP server with the RFC 8707 resource parameter:

GET /oauth/authorize
?response_type=code
&client_id=https://client.example.com/.well-known/oauth-client.json
&redirect_uri=http://localhost:9876/callback
&scope=mcp:echo mcp:search
&resource=https://mcp.yourcompany.com/mcp
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&state=xyz123

The user sees a consent screen listing the requested scopes. If an upstream IdP is configured, the user authenticates there first via SSO.

After consent, the client exchanges the authorization code for tokens:

POST /oauth/token HTTP/1.1
Host: auth.yourcompany.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=auth_code_here
&redirect_uri=http://localhost:9876/callback
&client_id=https://client.example.com/.well-known/oauth-client.json
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

AuthPlane returns an RFC 9068 JWT access token (typ: at+jwt, with iss, sub, aud, exp, jti, client_id, scope claims — aud set from the resource parameter) plus a refresh token. Refresh tokens rotate on every use; reuse of a consumed token revokes the whole family. With DPoP enabled, tokens are additionally bound to a client-held key via the cnf.jkt claim.

Step 5: JWT verification in your MCP server

Section titled “Step 5: JWT verification in your MCP server”

Your MCP server validates tokens with an AuthPlane SDK — discovery, JWKS caching, audience binding, and scope enforcement in a few lines:

auth = await authplane_auth(
issuer=os.environ["AUTHPLANE_ISSUER"],
base_url=os.environ["AUTHPLANE_BASE_URL"],
scopes=["mcp:echo"],
)
mcp = FastMCP("demo-server", **auth)

See the full integration guides: Python, TypeScript, Go.

WhatWhereWho controls it
User credentialsYour IdP (Okta, Entra, Google, Auth0)You
OAuth tokens & audit logAuthPlane database (PostgreSQL/SQLite)You
Vaulted upstream tokensAuthPlane database, encrypted at restYou
JWKS public keysAuthPlane /.well-known/jwks.jsonYou
MCP server dataYour databaseYou

AuthPlane runs in your infrastructure. No data leaves your perimeter, and the server sends no telemetry home.

AuthPlane can delegate user authentication to your existing identity provider — Google, Okta, Microsoft Entra ID, Auth0, or any OIDC-compliant provider. Enable it with AUTHPLANE_OIDC_ENABLED=true and configure providers in YAML; unauthenticated users are redirected to your IdP for SSO, then return to AuthPlane to approve the MCP client’s scope request.

For enterprise-managed authorization where the IdP mediates every agent-to-tool connection (XAA / ID-JAG via RFC 7523 JWT Bearer), see the federation guides.

Terminal window
# Development — SQLite, auto-generated keys, zero config
docker run -p 9000:9000 -p 9001:9001 \
-e AUTHPLANE_ADMIN_API_KEY -e AUTHPLANE_SESSION_SECRET \
-v authserver-data:/data \
authplane/authserver:latest serve

Production runs the same binary against PostgreSQL, with multiple instances behind a load balancer sharing one database (cache invalidation via LISTEN/NOTIFY). JWT verification is stateless, so your MCP servers keep working even if AuthPlane is briefly down.

Continue with the Docker guide, the Kubernetes guide, and the topology catalog.