# `ConduitMcp.Client.OAuth.Discovery.Fetch`
[🔗](https://github.com/nyo16/conduit_mcp/blob/v0.11.0/lib/conduit_mcp/client/oauth/discovery/fetch.ex#L2)

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).

# `context`

```elixir
@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.

# `discover`

```elixir
@spec discover(
  ConduitMcp.Client.OAuth.Config.t(),
  ConduitMcp.Client.OAuth.Challenge.t() | nil
) ::
  {:ok, context()} | {:error, ConduitMcp.Client.OAuth.Error.t()}
```

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`

```elixir
@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`

```elixir
@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).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
