Skip to main content

Module oauth_metadata

Module oauth_metadata 

Source
Expand description

OAuth 2.0 Protected Resource Metadata for the MCP endpoints.

Implements RFC 9728 so MCP-aware clients (e.g. Claude Desktop’s Custom Connectors, ChatGPT remote MCP) can discover the authorization server they need to authenticate against in order to call our MCP endpoints.

The discovery document is published at /.well-known/oauth-protected-resource and is public (no auth). Clients reach it either by following the resource_metadata parameter emitted in a WWW-Authenticate challenge from a 401 on /api/mcp/* (see crate::http::AuthError), or by probing the well-known URI directly.

§Host derivation

The resource field and the resource_metadata URL embedded in the 401 challenge are both absolute URLs and therefore depend on knowing the externally-visible host of this environment. We resolve that in this order:

  1. HttpConfig::http_host_name (set by the operator).
  2. The request’s Host header.

We deliberately do not consult X-Forwarded-Host or X-Forwarded-Proto: environmentd has no proxy-trust configuration today, and trusting those headers blind would let any client reaching the server directly poison the published metadata URLs (a host-header injection on the OAuth flow). Deployments behind a load balancer that rewrites Host are expected to set http_host_name explicitly.

§When the document is published

The handler returns 404 when no OAuth flow is meaningful for the listener: either because oidc_issuer is unset (no authorization server to advertise) or because the listener’s authenticator is listeners::AuthenticatorKind::None (no token would ever be validated). Returning an empty or fake document instead would mislead clients into starting an OAuth dance they cannot complete.

Structs§

McpOAuthConfig 🔒
Per-listener config shared by the OAuth discovery handler and the auth middleware’s WWW-Authenticate builder. Carried as an axum Extension because one environmentd process can serve listeners with different authenticators and http_host_names.
OauthMetadataMetrics
Prometheus counter for the discovery endpoint, labeled by the MetricStatus of each request so dashboards key off names rather than HTTP status codes.
ProtectedResourceMetadata 🔒
JSON shape returned by PROTECTED_RESOURCE_METADATA_PATH. A strict subset of RFC 9728 §2; further fields can be added without breaking clients per the RFC’s extensibility guidance.

Enums§

McpOAuthDiscovery 🔒
Whether and how an HTTP listener advertises OAuth 2.0 for its MCP routes. Derived once per listener from its authenticator and consulted by both the 401 WWW-Authenticate challenge and this discovery handler, so the two never disagree about whether OAuth is on offer.
MetricStatus 🔒
Closed set of outcomes recorded by OauthMetadataMetrics. Keeping these as an enum (rather than free-form strings at the call sites) pins the metric’s label cardinality and stops typos from silently creating new label values.

Constants§

ADAPTER_WAIT_TIMEOUT 🔒
Bounds how long the unauthenticated discovery handler waits for the adapter client. Without this, a wedged adapter could pin connections indefinitely from any caller on the network.
MCP_SCOPE 🔒
OAuth scope advertised for the MCP endpoints. Not enforced server-side (authorization is at the SQL layer via RBAC).
METADATA_CACHE_CONTROL 🔒
private (not the RFC-default public): the document varies by host, so shared caches must not serve one listener’s document to another.
PROTECTED_RESOURCE_METADATA_PATH 🔒
The well-known path served by this module.
PROTECTED_RESOURCE_METADATA_PATH_AGENT 🔒
Path-suffixed aliases of PROTECTED_RESOURCE_METADATA_PATH per RFC 9728 §3.1. Both MCP endpoints serve an identical metadata document today, so the same handler is mounted at all three paths; the aliases exist so strict clients that always probe with a path suffix do not have to fall back to the bare URI.
PROTECTED_RESOURCE_METADATA_PATH_DEVELOPER 🔒
PUBLISHED_SCHEME 🔒
OAuth 2.1 §3.1 requires https for all OAuth endpoints.

Functions§

handle_protected_resource_metadata 🔒
HTTP handler for PROTECTED_RESOURCE_METADATA_PATH.
metadata_url 🔒
Builds the absolute URL of the protected resource metadata document for use as the resource_metadata parameter in a WWW-Authenticate challenge. Returns None if the request lacks enough host information to construct a URL; the caller is expected to skip the Bearer challenge in that case rather than emit a malformed value.
resolve_host 🔒
Resolves the host string to embed in published absolute URLs.
validate_issuer_url 🔒
Validates oidc_issuer before it is published. Required: parses as a URL, scheme is https or http, no userinfo (we publish it on a public endpoint), no query or fragment (RFC 8414 §2). The http scheme is permitted to ease local dev; OAuth 2.1 §3.1 forbids it in production but enforcement is the operator’s responsibility.