Auth Server Docs Blog Pricing
GitHub ↗ Discord ↗ Get started →
AuthPlane Auth Server · Open Source

Your MCP server is exposed.
AuthPlane locks it down.

The self-hosted authorization server purpose-built for MCP. Open source under AGPL-3.0. One Go binary. Implements the complete MCP authorization spec (2025-11-25) — including full OAuth 2.1 — so every agent call is authenticated, every credential is managed, and every action is auditable.

OAuth 2.1 + PKCE Authorization code flow with proof key
RFC 7636
DPoP Token Binding Tokens bound to agent key — replay impossible
RFC 9449
Client Identity (CIMD) URL-based client identity — no registration needed
IETF Draft
Token Vault AES-256-GCM encrypted credential storage
Encrypted
Audit Trail Every auth event logged and queryable
Live
Cross-App Access (XAA) Enterprise IdP-mediated agent authorization
ID-JAG

What happens when agents can't authenticate.

How it works

From request to audit in six steps.

Follow a single agent from registration to audit. Every feature in context.

IDENTIFY

Agent presents its Client ID

The agent identifies itself using a Client ID Metadata Document (CIMD). It hosts a JSON document at a URL it controls — no registration endpoint needed.

Agent
client_id URL
AuthPlane
→ fetches client.json metadata
AUTHENTICATE

PKCE challenge sent

The agent initiates OAuth 2.1 with a code_challenge. AuthPlane validates and issues a single-use authorization code. No client secrets cross the wire.

Agent
code_challenge (S256)
AuthPlane
→ auth_code · expires: 600s
BIND TOKEN

DPoP binds token to key

The agent exchanges the code for an access token. DPoP binds it to the agent's ephemeral key pair — stolen tokens are useless.

DPoP JWT htm: POST htu: /token jti: unique
Agent Key alg: ES256 ephemeral
VEND CREDENTIAL

Vault returns scoped token

AuthPlane decrypts with per-owner HKDF derivation and returns a scoped token. The agent never sees raw credentials.

Vault
HKDF decrypt
Scoped Token
→ scope: repo:read · ttl: 3600s
CALL TOOL

Agent calls MCP server

The agent sends its DPoP-bound JWT and scoped credential. The server validates via AuthPlane's JWKS endpoint. Zero trust, fully verified.

Agent
DPoP JWT + credential
MCP Server
✓ Verified via JWKS
AUDIT

Every action logged

Authentication, credential vend, and tool call — all in one audit log. Queryable, exportable, and correlated with OpenTelemetry trace IDs.

Audit Trail● LIVE
14:23:01REGclient_id=agt_k9x
14:23:02AUTHpkce=S256
14:23:03DPOPjti=8f3a bound
14:23:04VAULTscope=repo:read
14:23:05TOOLgithub.list_issues ✓
How it works

From request to audit in six steps.

Follow a single agent from registration to audit. Every feature in context.

1
IDENTIFY

Agent presents its Client ID

The agent identifies itself using a Client ID Metadata Document (CIMD). It hosts a JSON document at a URL it controls — no registration endpoint needed.

2
AUTHENTICATE

PKCE challenge sent

The agent initiates OAuth 2.1 with a code_challenge. AuthPlane validates and issues a single-use authorization code. No client secrets cross the wire.

Agent
code_challenge (S256)
AuthPlane
→ auth_code · expires: 600s
3
BIND TOKEN

DPoP binds token to key

The agent exchanges the code for an access token. DPoP binds it to the agent's ephemeral key pair — stolen tokens are useless.

DPoP JWT htm: POST htu: /token jti: unique
Agent Key alg: ES256 ephemeral
4
VEND CREDENTIAL

Vault returns scoped token

AuthPlane decrypts with per-owner HKDF derivation and returns a scoped token. The agent never sees raw credentials.

Vault
HKDF decrypt
Scoped Token
→ scope: repo:read · ttl: 3600s
5
CALL TOOL

Agent calls MCP server

The agent sends its DPoP-bound JWT and scoped credential. The server validates via AuthPlane's JWKS endpoint. Zero trust, fully verified.

Agent
DPoP JWT + credential
MCP Server
✓ Verified via JWKS
6
AUDIT

Every action logged

Authentication, credential vend, and tool call — all in one audit log. Queryable, exportable, and correlated with OpenTelemetry trace IDs.

Audit Trail● LIVE
14:23:01REGclient_id=agt_k9x
14:23:02AUTHpkce=S256
14:23:03DPOPjti=8f3a bound
14:23:04VAULTscope=repo:read
14:23:05TOOLgithub.list_issues ✓
Admin Console

Full visibility. Zero guesswork.

A built-in admin console to manage clients, users, tokens, signing keys, and audit every event — in real time.

localhost:9001/admin/ui

Overview

v0.1.0 · up 3h 2m · 30s refresh
SIGNING KEY ES256 keyfile
ENCRYPTION not_configured
CONNECTORS disabled vault inactive
VAULT CONNECTIONS 0 6 users
CLIENTS25
USERS6
VAULT CONNS0
TOKENS (24H)25
REVOKED0
INFO DPoP enabled · nonce TTL 5m0s · agents enabled
INFO Token exchange enabled · max chain depth 5
RECENT AUDIT EVENTS
TIMEEVENTACTORDETAIL
35m agotoken.refreshedXK2bn4q0...Mo9HAfamily=Pt2NEtn...
35m agotoken.issuedXK2bn4q0...Mo9HAfamily=Pt2NEtn...
35m agoconsent.grantedXK2bn4q0...Mo9HA
35m agouser.loginXK2bn4q0...Mo9HA

Clients

25 registered OAuth clients
Search by name or ID... ALL PUBLIC CONFIDENTIAL AGENT
IDNAMETYPEGRANT TYPESSTATUS
Hpw66du...Orchestrator Agentconfidentialclient_credentials, token-exchange active
rIjcYSu...Planning Agentconfidentialclient_credentials, token-exchange active
CF1la24...Data Extraction Agentconfidentialclient_credentials active
ypDrr5u...Workflow Automatorpublicauthorization_code, refresh_token active
vZJuhFt...AI Code Assistantpublicauthorization_code, refresh_token active
ex5sdz5...monitoring-agentconfidentialclient_credentials, token-exchange active
L3Tjjo2...deploy-botconfidentialclient_credentials, token-exchange active
CLIENT DETAIL Orchestrator Agent ×
CLIENT_IDHpw66duHwe87knea8xkRKg
NAMEOrchestrator Agent
TYPEconfidential
GRANT_TYPESclient_credentials token-exchange
RESPONSE_TYPEScode
AUTH_METHODclient_secret_basic
STATUS active
REDIRECT_URISnone
REGISTRATIONadmin
ISSUED_ATMar 12, 2026
UPDATED_ATMar 12, 2026
ACTIONS

Users

6 registered users
Search by name, email, or ID...
IDNAME / EMAILAUTHROLESTATUS
Ghrn8H1...demo@example.comlocaladmin active
1nTDvIF...Alice Chenalice@example.comlocaluser active
1gqkoFq...Bob Martinezbob@example.comlocaluser active
0oS18xT...Carol Smithcarol@example.comlocaluser active
RIs-0x3...Dave Wilsondave@example.comlocaluser active
XK2bn4q...Admin Useradmin2@example.comlocaladmin active
USER DETAIL Alice Chen ×
USER_ID1nTDvIFwlDSKGDkW4nX_ZQ
NAMEAlice Chen
EMAILalice@example.com
ROLEuser
STATUS active
PROVIDERlocal
CREATEDMar 12, 2026
ACTIONS

Tokens

25 issued tokens
Search by JTI, client, user, or scope... ALL AUTH_CODE CLIENT_CREDS
JTITYPECLIENTUSERSCOPESTATUSCREATED
Pt2NEtnl0tZf...auth_codeypDrr5u2P0Fk...XK2bn4q0QQ...tools/add_numbers tools/get_ti... activeMar 12, 2026
epIexC2RtbDr...auth_codevZJuhFt_ytrN...XK2bn4q0QQ...tools/add_numbers tools/echo t... activeMar 12, 2026
-YDk9E_DgG-4...auth_coderP1x81eFjM4c...XK2bn4q0QQ...tools/add_numbers tools/echo activeMar 12, 2026
_D7bWp3_eogL...auth_codeLYSkZVE04ggx...XK2bn4q0QQ...tools/echo tools/get_time activeMar 12, 2026
L_kB_xozaSIa...auth_codeitSUogeTzP0j...XK2bn4q0QQ...tools/add_numbers tools/echo t... activeMar 12, 2026
o6z2Nxhl6kuK...auth_codeypDrr5u2P0Fk...RIs-0x33Gvek...tools/add_numbers tools/get_ti... activeMar 12, 2026
tJ2TsqiCn-t5...auth_codevZJuhFt_ytrN...RIs-0x33Gvek...tools/add_numbers tools/echo t... activeMar 12, 2026
TOKEN DETAIL Pt2NEtnl0tZf... ×
JTIPt2NEtnl0tZf-zruh9AUuQ
TYPEauth_code
CLIENT_IDypDrr5u2P0FkD_kT8W_tyA
USER_IDXK2bn4q0QTE9sKQUMo9HA
SCOPEtools/add_numbers tools/get_time
RESOURCE
STATUS active
CREATED_ATMar 12, 2026, 01:54 PM
EXPIRES_AT
ACTIONS

Audit Log

50 events · 30s auto-refresh
Live
Filter by action... Filter by actor ID...
TIMESTAMPEVENTACTORDETAIL
13:54:18token.refreshedXK2bn4q0QQTE9s...family=Pt2NEtnl0tZf-zruh9AUuQ
EVENTtoken.refreshed
TIMESTAMP2026-03-12T20:54:18.000Z
ACTOR_IDXK2bn4q0QQTE9sKQUMo9HA
ACTOR_TYPE
CLIENT_IDypDrr5u2P0Fk_kT8W_tyA
DETAILfamily=Pt2NEtnl0tZf-zruh9AUuQ
13:54:18token.issuedXK2bn4q0QQTE9s...family=Pt2NEtnl0tZf-zruh9AUuQ
13:54:18consent.grantedXK2bn4q0QQTE9s...
13:54:18user.loginXK2bn4q0QQTE9s...
13:54:17token.refreshedXK2bn4q0QQTE9s...family=eplexC2RtbDrg...
13:54:17token.issuedXK2bn4q0QQTE9s...family=eplexC2RtbDrg...
13:54:17consent.grantedXK2bn4q0QQTE9s...
13:54:17user.loginXK2bn4q0QQTE9s...
13:54:16token.revokedmachine_token jti=3nnzYp...

System

Server health and configuration — all values sanitized
AUTHPLANEv0.1.0up 3h 26m 49s
STORAGEsqlite healthy
ENCRYPTION not_configured
RATE LIMITINGenabled active
SERVER CONFIGURATION
ISSUERhttp://localhost:9000
SIGNING ALGORITHMES256
KEY STOREkeyfile
STORAGE DRIVERsqlite
ENCRYPTION DRIVER
CLIENT IDcimd
FEATURE FLAGS
CLIENT CREDENTIALSenabled
RFC 6749 §4.4 machine-to-machine tokens
DPOPenabled
Nonce TTL: 5m0s · Require nonce: no
TOKEN EXCHANGEenabled
Max chain depth: 5
AGENT IDENTITYenabled
JWKS listing: no
TOKEN VAULTdisabled
Encrypted token storage for upstream providers
OIDC LOGINdisabled
External identity provider login

Six capabilities. One server.

Every feature purpose-built for MCP. Click to explore the details.

USER PROVIDER VAULT AGENT AES-256-GCM

Credentials in. Scoped tokens out.

  • AES-256-GCM encryption with HKDF key derivation
  • User-authorized PKCE flows per provider (GitHub, Slack, Linear, Jira)
  • Vend scoped tokens at tool-call time — agents never see raw credentials
  • HashiCorp Vault Transit backend for regulated environments
ES256 PRIVATE KEY DPoP ✓ ACCESS TOKEN BOUND VERIFY PROOF-OF-POSSESSION
RFC 9449

Tokens bound to their owner.

  • ES256 ephemeral key pair per client — tokens can't be replayed
  • Per-request proof with JTI nonce and server validation
  • Automatic nonce rotation and replay detection
  • Works seamlessly with Token Vault and Token Exchange
ORCHESTRATOR FULL SCOPE RFC 8693 EXCHANGE WORKER SCOPED ↓ SCOPE DOWNSCOPING read:write:admin read
RFC 8693

Delegate without oversharing.

  • Orchestrator → worker delegation with automatic scope downscoping
  • Chain depth tracked and enforced in token claims
  • Every exchange audited with actor and target identifiers
  • Supports both impersonation and delegation grant types
JWT TOKEN CLAIMS agent_name "data-worker" sub "agent:a1b2c3" scope "read:issues" act.sub "user:diego" chain_depth 2 aud "linear-mcp" VERIFIED

Every agent, fully identified.

  • Unique agent identity with agent_name and sub claims
  • Embedded user context via act (actor) claim chain
  • Fine-grained scope breakdown per agent in the token
  • Built on OpenID Connect ActorToken extension
SERVICE client_secret POST /oauth/token AUTH SERVER 200 OK MACHINE TOKEN NO USER CONTEXT REQUIRED
RFC 6749

Machine to machine. No user needed.

  • Direct access tokens for service accounts — no user context
  • Scope-based access control per service with CLI management
  • Automatic rate limiting and quota enforcement
  • Full audit trail for every machine token issued
USER (SSO) POLICY CHECK ENTERPRISE IdP ID-JAG JWT jwt-bearer VALIDATE AUTHPLANE ACCESS TOKEN JWKS NO CONSENT SCREEN REQUIRED
ID-JAG

Enterprise IdP in the loop.

  • Enterprise IdP (Okta, Entra ID) mediates every agent-to-tool connection
  • JWT Bearer grant (RFC 7523) — no user-facing consent screens needed
  • Trusted IdP registry with JWKS discovery and audience binding
  • Policy engine enforces scope, group membership, and step-up auth

Built to the letter of the spec.

Every RFC the MCP authorization specification requires. Verified with MCP Inspector.

RFC Specification What it means for you Status
MCP 2025-11-25 MCP Authorization The complete MCP authorization spec, end to end. Tested with Claude Code, Claude Desktop, MCP Inspector. Implemented
RFC 8414 AS Metadata MCP clients auto-discover your authorization server config from a well-known endpoint. Implemented
RFC 9728 Protected Resource Metadata Your MCP server declares its AS. Clients find it automatically. Zero manual configuration. Implemented
RFC 7636 PKCE (S256 only) Proof Key for Code Exchange. Code interception prevented. plain method explicitly rejected. Implemented
CIMD + DCR Client Identity CIMD (URL-based) is the default. DCR (RFC 7591) supported as alternative for legacy clients. Implemented
RFC 8707 Resource Indicators Tokens are audience-bound to specific MCP servers. Cross-server token replay prevented. Implemented
RFC 7009 Token Revocation Immediate server-side revocation. Access and refresh tokens invalidated independently. Implemented
RFC 9068 JWT Profile Structured access tokens. jti, iss, sub, aud, scope, exp, iat in every issued token. Implemented
RFC 9449 DPoP Sender-constrained tokens bound to a client-held key. Server nonces and JTI replay prevention built in. Implemented
RFC 8693 Token Exchange Impersonation and delegation with nested act chains, scope narrowing, and configurable depth limits. Implemented
RFC 7662 Token Introspection Server-side token state checks for confidential clients. Pairs with RFC 7009 revocation. Implemented
RFC 7523 · ID-JAG JWT Bearer / XAA Enterprise IdP-mediated authorization: trusted IdP registry, JWKS discovery, subject mapping. Implemented
RFC 9700 OAuth Security BCP PKCE required, exact redirect matching, refresh rotation, no implicit grant, no ROPC. Implemented
RFC 9457 Problem Details Structured error responses with machine-readable type URIs and human-readable descriptions. Implemented
Security properties Enforced at runtime, not optional
  • Redirect URI Exact match required — no wildcards, no partial matches
  • alg=none Unsigned tokens rejected at the verification layer
  • Key rotation Automated JWKS rotation with zero-downtime key rollover
  • Token binding Audience claim bound to resource at issuance, validated on receipt
  • Scope enforcement Token scopes validated against registered server permissions
  • Clock skew Configurable tolerance window for exp/nbf/iat validation
Tested with Claude Code, Claude Desktop, and MCP Inspector. Full matrix in the standards reference.

Protect your server in 5 minutes.

Two env vars. Point your SDK at AuthPlane. PKCE, CIMD, JWKS rotation — all handled automatically.

# server.py — FastMCP + authplane-fastmcp
from fastmcp import FastMCP
from authplane_fastmcp import authplane_auth

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

mcp = FastMCP("my-server", **auth)

@mcp.tool()
def echo(text: str) -> str:
    return text
// server.ts — @authplane/mcp
import { authplaneMcpAuth } from "@authplane/mcp";

const auth = await authplaneMcpAuth({
  issuer: process.env.AUTHPLANE_ISSUER!,
  resource: process.env.AUTHPLANE_RESOURCE!,
  scopes: ["mcp:echo"],
});

app.get(
  auth.protectedResourceMetadataPath,
  auth.protectedResourceMetadataHandler,
);
app.use("/mcp", auth.bearerAuth);
// main.go — authplane/go-sdk
import "github.com/authplane/go-sdk/mcp/pkg/authplanemcp"

adapter, err := authplanemcp.NewAdapter(ctx, authplanemcp.Options{
    Issuer:   os.Getenv("AUTHPLANE_ISSUER"),
    Resource: os.Getenv("AUTHPLANE_RESOURCE"),
    Scopes:   []string{"mcp:echo"},
})

http.Handle(adapter.WellKnownPRMPath(),
    adapter.ProtectedResourceMetadataHandler())
http.Handle("/mcp", adapter.AuthMiddleware(handler))
Two env vars
AUTHPLANE_ISSUER plus your resource URL. Discovery, JWKS, and rotation are automatic.
Per-tool scopes
Enforce fine-grained access at the individual tool level, not just server-wide.
Offline validation
JWKS is cached. No round-trip to AuthPlane on every request — adds zero latency.

Three paths. One server.

Same server, different configurations. Pick the path that matches your environment.

Local Development

SQLite · Zero config

docker run -p 9000:9000 -p 9001:9001 \
  -e AUTHPLANE_ADMIN_API_KEY \
  authplane/authserver:latest serve

Production

PostgreSQL · HA ready

helm install authplane \
  oci://ghcr.io/authplane/charts/authplane \
  --values prod.yaml

Regulated Enterprise

Vault Transit · Air-gapped

authserver serve \
  --config /etc/authplane/config.yaml

Start building with AuthPlane.

Open source. Self-hosted or cloud-hosted. No credit card required.