Skip to content

v0.2.0

Choose a tag to compare

@Agent-Hellboy Agent-Hellboy released this 18 Sep 07:52
· 23 commits to main since this release
8f6e880

Provider-neutral OAuth for MCP resource servers: authorization server plus Python and Go SDKs.

This release makes a real MCP client work end to end against a path-mounted issuer. Every fix below was found by a client failing where curl succeeded.

Breaking

MCP_AUTH_TRUST_PROXY_TLS defaults to false. X-Forwarded-Proto is set by the caller, so it was never evidence of TLS — anything able to reach the process satisfied MCP_AUTH_REQUIRE_HTTPS by sending one header. Verified against a live cluster, where an unrelated pod reached the authorization server and got 200.

A deployment behind a TLS-terminating proxy must now set MCP_AUTH_TRUST_PROXY_TLS=true, or every request is refused with https_required. The rejection names the setting. Treat it as a claim about your topology and enforce it with a NetworkPolicy or equivalent, so the proxy really is the only route in.

Authorization server

  • Serve RFC 8414 metadata at the path-insertion location for a path-mounted issuer. Clients request /.well-known/oauth-authorization-server/<path>, not <issuer>/.well-known/..., so discovery previously 404'd.
  • Post the consent form to the issuer-mounted path. A root-relative action 404'd before the upstream identity provider was ever reached.
  • /register echoes client_id_issued_at, grant_types, response_types, and scope (RFC 7591). Clients that cannot read back what they registered re-register on every attempt.
  • Advertise subject_types_supported and id_token_signing_alg_values_supported. Clients validating against the OpenID Connect Discovery schema rejected the whole document without them.
  • Protected resource metadata is served per resource. It previously answered the bare well-known path with resources[0] plus a non-standard resources member, handing multi-resource deployments an audience the client never asked for.

SDKs

  • ProtectedResourceMetadataHandler (Go) and protected_resource_metadata (Python) derive scopes_supported from the verifier's required scopes, so the advertised set cannot drift from the enforced one. A server that omits it leaves the client nothing to request; the token carries no scope and every call fails 403 with nothing explaining why.
  • unauthorized_headers_for_error (Python), matching Go's existing helper, so a Python resource server can emit insufficient_scope and let a client retry.
  • unauthorized_headers (Python) now comma-separates auth-params per RFC 9110 section 11.6.1. Lenient parsers accepted the old form; strict ones did not.
  • Discovery in both clients tries the RFC 8414 forms before the OIDC suffix forms.

Docs

New security model page. The README is now an index; its duplicated setup, commands and release sections were already in docs/development.md. The deliberate RFC 8252 private-use-scheme deviation from the spec's localhost-or-HTTPS rule is documented, since desktop MCP clients cannot work without it.

Verified

Full flow against a live deployment for both a Go and a Python resource server: discovery, DCR, PKCE S256, Keycloak login, token with bound audience, initialize, tools/list, tools/call. Cross-audience tokens rejected 401 both ways. Confirmed from Cursor as a real client.

Modules

  • github.com/Agent-Hellboy/mcp-auth/auth-server at auth-server/v0.2.0
  • github.com/Agent-Hellboy/mcp-auth/auth-client/go at auth-client/go/v0.2.0
  • Python mcp-auth-client 0.2.0

🤖 Generated with Claude Code