ConduitMcp.Client.OAuth.Discovery.Fetch (ConduitMCP v0.11.0)

Copy Markdown View Source

The network side of authorization-server discovery: fetches the candidates ConduitMcp.Client.OAuth.Discovery lists, in order, and validates what comes back with its pure validators. Compiled only with the optional :finch dependency.

discover/2, for the MCP server of a ConduitMcp.Client.OAuth.Config:

  1. Protected Resource Metadata (RFC 9728) - the challenge's resource_metadata URL, then the path-inserted, then the root well-known URL. A candidate that fails (any non-200 status, a redirect, a network error, a body that is not a JSON object) is skipped; the first document is validated, and a document naming another resource is refused (:resource_mismatch) rather than skipped. None ⇒ :no_protected_resource_metadata.
  2. The authorization server - the first of authorization_servers, which must be in :expected_issuers when that is set.
  3. Authorization-server metadata (RFC 8414 / OpenID Connect Discovery) - OAuth path-insert, OIDC path-insert, OIDC path-append (without an issuer path: OAuth, then OIDC). The first document is validated: exact issuer, S256, https endpoints (OA-1, OA-4, OA-7). None ⇒ :no_authorization_server_metadata.

Every request goes through the client's OAuth HTTP layer: no Authorization header, no redirects, a capped body, and every URL checked by ConduitMcp.Client.URLPolicy :public_only and pinned to the vetted address (HT-6) unless allow_insecure_oauth: true admits a plain-http loopback URL (development and tests).

Summary

Types

What discovery found: the canonical resource, the validated PRM, the chosen issuer and its validated metadata. No secrets.

Functions

Discovers the authorization server of config's MCP server, using the resource_metadata of challenge (a Bearer ConduitMcp.Client.OAuth.Challenge) when it has one.

Fetches and validates the metadata of the authorization server issuer (see the moduledoc for the order).

Fetches and validates the Protected Resource Metadata for the canonical resource (see the moduledoc for the order).

Types

context()

@type context() :: %{
  resource: String.t(),
  prm: map(),
  issuer: String.t(),
  metadata: map()
}

What discovery found: the canonical resource, the validated PRM, the chosen issuer and its validated metadata. No secrets.

Functions

discover(config, challenge \\ nil)

Discovers the authorization server of config's MCP server, using the resource_metadata of challenge (a Bearer ConduitMcp.Client.OAuth.Challenge) when it has one.

fetch_as_metadata(config, issuer)

@spec fetch_as_metadata(ConduitMcp.Client.OAuth.Config.t(), String.t()) ::
  {:ok, map()} | {:error, ConduitMcp.Client.OAuth.Error.t()}

Fetches and validates the metadata of the authorization server issuer (see the moduledoc for the order).

fetch_prm(config, resource, resource_metadata)

@spec fetch_prm(ConduitMcp.Client.OAuth.Config.t(), String.t(), String.t() | nil) ::
  {:ok, map()} | {:error, ConduitMcp.Client.OAuth.Error.t()}

Fetches and validates the Protected Resource Metadata for the canonical resource (see the moduledoc for the order).