MCP authorization
MCP OAuth 2.1 authorization server
Secure an MCP server with an authorization server you host. theAuth implements the discovery, registration and token steps MCP clients expect, and adds agent identity and delegation chains behind the token.
- RFCs
- 9728 8707 8414 7591
- PKCE
- S256 only
- Spec
- MCP 2025-11-25
- Resource module
- 0 deps
Discover, register, get a token
What an MCP client does, step by step
The client starts knowing only the MCP server URL. Everything else is discovered.
- Client to resource server
Call without a token
The MCP server answers 401 with a
WWW-Authenticate: Bearerheader that points at its resource metadata (RFC 6750, RFC 9728). - Client to resource server
Read protected resource metadata
GET /.well-known/oauth-protected-resourcenames the resource and its authorization servers (RFC 9728). - Client to authorization server
Read server metadata
GET /.well-known/oauth-authorization-serverlists endpoints andcode_challenge_methods_supported: ["S256"](RFC 8414). - Client to authorization server
Register
Dynamic registration (RFC 7591), or in Go a CIMD client whose
client_idis an https URL the server fetches (MCP spec 2025-11-25). - Client to authorization server
Authorize with PKCE
The client sends a PKCE S256 challenge and a
resourceindicator (RFC 8707). The user consents and a code returns. - Client to authorization server
Exchange the code
The code and verifier go to the token endpoint. The access token is bound to the audience.
- Client to resource server
Call with the token
The MCP server validates signature, audience, scope and expiry before running the tool.
/mcp/register, /mcp/authorize and /mcp/token. The Go server serves /oauth/register, /oauth/authorize and /oauth/token.Go
CIMD, rotation and the resource module
- CIMD. When enabled, a
client_idthat is an https URL is resolved by fetching a metadata document. The document'sclient_idmust equal the URL it was fetched from. The trust policy defaults to deny all; you opt in withAllowHTTPSHostsorAllowAnyHTTPS. - Refresh rotation with family revocation. Presenting an already-used refresh token revokes the whole token family (RFC 9700).
- mcpresource. A standalone Go module with no dependencies outside the standard library. It checks the JWT against a cached JWKS, enforces audience, and walks the RFC 8693 actor chain through introspection. Revocation reaches it within the cache TTL, 60 seconds by default.
- More Go grants. RFC 8693 token exchange, 9449 DPoP, 9126 PAR, 9101 JAR and 9509 CIBA.
// Resource server: go get github.com/glincker/theauth-go/mcpresource
v := mcpresource.New(
"https://mcp.example.com",
mcpresource.WithJWKS("https://as.example.com/oauth/jwks"),
mcpresource.WithIntrospection(
"https://as.example.com/oauth/introspect",
"mcp-client-id", "mcp-client-secret",
),
)
r := chi.NewRouter()
r.Use(v.Middleware) // JWT validation, audience check, actor-chain walk
r.Get("/tools/run", func(w http.ResponseWriter, r *http.Request) {
p, _ := v.Principal(r.Context())
// p.Subject (user), p.Actor (final agent), p.ActorChain (delegation path)
})
Runnable example: examples/mcp-server.
import { createTheAuth } from "@glinr/theauth";
const auth = await createTheAuth({
database: { provider: "postgres", url: process.env.DATABASE_URL },
mcp: {
enabled: true,
issuer: "https://auth.yourapp.com",
baseUrl: "https://auth.yourapp.com",
signingSecret: process.env.MCP_SIGNING_SECRET,
},
});
TypeScript
Enable it in the core
Mount the MCP routes through a framework adapter, then validate tokens with auth.mcp.validate(token) or the adapter's withMcpAuth middleware. TypeScript access tokens are HS256 at+jwt signed with your secret, so validation happens in the same deployment. The Go server signs with Ed25519 and publishes a JWKS, which lets separate resource servers verify tokens on their own.
Where it fits
What differs from other options
Other identity products also document MCP OAuth support, so the protocol alone is not the difference. What theAuth adds:
- Agent identity and delegation chains behind the same tokens, with per-agent audit.
- MIT license and self-hosting on your own database.
- A zero-dependency Go resource-server module.
you already run a hosted identity provider that meets your MCP needs and you do not need agent-level identity or self-hosting.
Get started
Give your first agent an identity.
Install the package, create an agent with scoped permissions, and read its first audit record. The core runs on Postgres, SQLite, MySQL or D1, and the Go module needs a single go get.
npm install @glinr/theauthgo get github.com/glincker/theauth-go