The one rule for a URL fetched server-side: https, a real hostname, and
nothing that resolves into the cluster. OAuth clients, MCP discovery and
dynamic client registration all GET/POST tenant-supplied addresses from
inside the platform, which is exactly the request an SSRF wants — so every
one of them passes through check/1 first, and again on each use, since DNS
can change between a save and a fetch.
Rejected: any scheme but https, an IP literal, localhost and the
cluster-internal names, and a hostname that resolves only to loopback,
private (RFC 1918), link-local, CGNAT or the metadata range. A hostname
that does not resolve is also refused: nothing could be fetched from it,
and a name that resolves later to something private is the trick.
:allow_private_hosts can be passed to check/2 to turn the resolution
check off so a local stub can stand in for a provider. Its default comes
from config :managoat_mcp_auth, allow_private_hosts: false.
Summary
Functions
:ok, or {:error, reason} naming the rule the URL broke.
Why a URL was refused, for a form or an API error.