Managoat.McpAuth.Discovery (managoat_mcp_auth v0.1.0)

Copy Markdown View Source

How a client learns where a remote MCP server's authorization lives, the way the MCP authorization spec says:

  1. GET the MCP URL with no credential. A conforming server answers 401 with WWW-Authenticate: Bearer resource_metadata="…" naming its protected-resource metadata (RFC 9728). A server that names none is tried at the well-known path for its URL, then at the origin's.
  2. That document's authorization_servers names the issuer; its RFC 8414 metadata (/.well-known/oauth-authorization-server, with the OpenID /.well-known/openid-configuration as the fallback) carries the authorize, token, revocation and registration endpoints.
  3. register/3: RFC 7591 dynamic client registration at registration_endpoint, so the tenant types no client id anywhere. The server picks the client's auth method; a public client comes back with no secret and token_endpoint_auth_method: none.

Every URL, including the ones the server sent back, passes Managoat.McpAuth.UrlGuard before it is fetched: a malicious resource document that points discovery at the metadata service is the obvious trick. Each fetch has a timeout and the chain follows no redirects.

Summary

Functions

The merged metadata for an MCP server URL: resource, issuer, authorization_endpoint, token_endpoint, revocation_endpoint, registration_endpoint, scopes, code_challenge_methods_supported, token_endpoint_auth_methods_supported, plus the raw documents under resource_metadata and authorization_server_metadata.

Register a client at the authorization server (RFC 7591) and return the provider attributes it yields: client_id, client_secret (absent for a public client), token_endpoint_auth and client_source: "dcr".

Types

metadata()

@type metadata() :: %{required(String.t()) => term()}

Functions

discover(mcp_url)

@spec discover(String.t()) :: {:ok, metadata()} | {:error, term()}

The merged metadata for an MCP server URL: resource, issuer, authorization_endpoint, token_endpoint, revocation_endpoint, registration_endpoint, scopes, code_challenge_methods_supported, token_endpoint_auth_methods_supported, plus the raw documents under resource_metadata and authorization_server_metadata.

register(md, redirect_uri, opts)

@spec register(metadata(), String.t(),
  client_name: String.t(),
  client_uri: String.t()
) ::
  {:ok, map()} | {:error, term()}

Register a client at the authorization server (RFC 7591) and return the provider attributes it yields: client_id, client_secret (absent for a public client), token_endpoint_auth and client_source: "dcr".

:client_name and :client_uri are required options. Omitting either is a programming error and raises rather than sending an incomplete registration.