Fetch and read OAuth 2.0 / OpenID Connect authorization-server metadata (RFC 8414 / OpenID Connect Discovery 1.0).
A thin lookup: it fetches the discovery document (and, separately, the JWKS)
and returns the parsed JSON. It runs no flow and holds no state - the host
reads the endpoint URLs and capabilities it needs (and, for verifying JARM
responses, passes the fetched JWKS to AttestoClient.JARM.verify/3).
URL construction
By default the OpenID Connect Discovery document is fetched by appending
/.well-known/openid-configuration to the issuer (OpenID Connect Discovery
1.0 §4). Pass well_known: :oauth_authorization_server for the RFC 8414
document, which is constructed differently: the well-known segment is inserted
before the issuer's path component (RFC 8414 §3.1), so it is correct for
path-based (multi-tenant) issuers.
Issuer / transport validation
- The issuer MUST be an
httpsURL with no query or fragment (RFC 8414 §2). - The document's own
issuermember MUST be identical to the issuer it was fetched for (RFC 8414 §3.3 / OpenID Connect Discovery 1.0 §4.3), defending against a metadata mix-up. The comparison is exact - no trailing-slash normalisation - so a path-based issuer that ends in/(e.g. a multi-tenant issuer) must be supplied exactly as the server publishes it. A trailing slash is removed only when constructing the well-known request URL, as both specs require. - A JWKS is fetched only over
https, since it is the trust root for verifying the authorization server's signatures.
HTTP
Requests go through Req. Pass :req_options
for safe transport configuration - notably plug: for Req.Test in tests.
The library owns the destination, method, authority, redirects, request body,
cache policy, and TLS peer identity; conflicting options are removed or fail
closed. Discovery and JWKS requests never carry caller credentials. Their
overall deadline includes DNS screening and request preparation, not only the
socket receive phase.
Summary
Functions
Fetch the authorization server's metadata for issuer, returning
{:ok, metadata} (a string-keyed map) or {:error, reason}.
Fetch a JWKS document from jwks_uri (typically the metadata's jwks_uri),
returning {:ok, jwks} - a map with a "keys" list - or {:error, reason}.
The URI must be https.
Preflight an authorization-server endpoint against the URL and address policy. The endpoint must use HTTPS, must not contain userinfo or a fragment, and must not resolve to a private, loopback, or link-local address.
Types
@type error() :: :invalid_issuer | :invalid_well_known | :invalid_jwks_uri | :issuer_mismatch | :invalid_metadata | :response_too_large | :blocked_host | :unsafe_transport_options | :invalid_timeout | :invalid_resolver | :timeout | {:http_status, pos_integer()} | {:transport, term()}
@type opt() :: {:well_known, well_known()} | {:req_options, keyword()} | {:max_response_bytes, pos_integer()} | {:timeout, pos_integer()} | {:resolver, (charlist(), :inet | :inet6 -> {:ok, [:inet.ip_address()]} | {:error, term()})}
@type well_known() :: :openid_configuration | :oauth_authorization_server
Functions
Fetch the authorization server's metadata for issuer, returning
{:ok, metadata} (a string-keyed map) or {:error, reason}.
issuer must be an https URL with no query or fragment. Options:
:well_known (:openid_configuration (default) or
:oauth_authorization_server) and :req_options (safely merged into the Req
transport configuration as described above). :timeout is the overall
discovery deadline in milliseconds, including DNS resolution and request
preparation (default: 10 seconds).
Fetch a JWKS document from jwks_uri (typically the metadata's jwks_uri),
returning {:ok, jwks} - a map with a "keys" list - or {:error, reason}.
The URI must be https.
@spec validate_endpoint( term(), keyword() ) :: :ok | {:error, :invalid_endpoint | :blocked_host | :invalid_resolver}
Preflight an authorization-server endpoint against the URL and address policy. The endpoint must use HTTPS, must not contain userinfo or a fragment, and must not resolve to a private, loopback, or link-local address.
This check does not bind a later, separate socket connection to the address it inspected, so it is not by itself a complete SSRF defense. Prefer this library's request APIs, which carry the screened address through to the transport while retaining the original hostname for TLS and HTTP authority.
Applications normally use this indirectly through the authorization-code, refresh, and revocation APIs.