Discover Authorization Without Creating an SSRF Proxy
Follow standards-defined metadata while validating every network destination before use. • Lab status: conceptual reconstruction. Receipts are expected simulated outputs, not deployed-system measurements.
System map · Day 03
Whole-system design
Five stable layers. Today's work is expanded and linked; the rest stays in context.
Entry and policy
Covered — Client entryAhead — Browser and callback
Onboarding and control
Connector control plane
Design target · not proved
Treats every discovered URL and capability as untrusted input before applying it.
Authorization services
Authorization server
Design target · not proved
Publishes endpoint and capability metadata without becoming trusted merely by discovery.
Compute and execution
Covered — Connector runtime computeAhead — Refresh coordinator compute
External resource server
Design target · not proved
Identifies its authorization server through protected-resource metadata or a challenge.
Storage and evidence
Covered — Specification draft store · Connector configuration storeAhead — Secret and token vault · Ordered migration log
Traversed today
Overview
Why discovery expands the trust boundary
Day 02 produced a connector draft with a protected resource URL. OAuth protected-resource metadata can point the gateway to an authorization server, whose metadata then supplies authorization and token endpoints. Each hop is attacker-influenced network input, so blindly following it can cause server-side request forgery (SSRF): making the gateway fetch an unintended internal service.
Today you will resolve one protected-resource chain, sanitize every URL before connection and after redirect, cache only the validated result, and produce SCG-R03. The discovery sequence follows RFC 9728, RFC 8414, OpenID Connect Discovery, and the MCP 2025-11-25 authorization profile.
Resolve metadata through a guarded fetcher
Connector control first uses a WWW-Authenticate resource metadata hint when present, then bounded well-known fallbacks. A guarded fetcher requires HTTPS outside explicit loopback development, resolves DNS, rejects private, loopback, link-local, multicast, and reserved ranges, binds the connection to a vetted address, and verifies the connected peer. That binding closes the DNS-rebinding gap between validation and fetch. It also limits bytes and redirects, and repeats resolution, binding, and peer verification for every redirect. Production code should use vetted URL, IP, and transport libraries rather than handwritten string tests.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://calendar.example/.well-known/oauth-protected-resource"
{
"issuer": "https://auth.example",
"authorization_endpoint": "https://auth.example/authorize",
"token_endpoint": "https://auth.example/token",
"registration_endpoint": "https://auth.example/register"
}
const policy = {
protocols: ["https:"],
maxRedirects: 2,
maxBytes: 65536,
rejectAddressClasses: ["private", "loopback", "link-local", "reserved"],
connectToResolvedAddress: true,
verifyConnectedPeerAddress: true,
revalidateRedirectTarget: true,
} as const;
Cache only a sanitized discovery receipt
The discovery cache stores normalized endpoints, issuer, source URL, validation timestamp, expiry, and policy version. It does not cache raw response headers, cookies, or arbitrary metadata fields. Cache lookup must still confirm that the requested resource and policy version match.
{
"cacheKey": "calendar-lab|https://calendar.example/mcp|policy-v1",
"issuer": "https://auth.example",
"tokenEndpoint": "https://auth.example/token",
"expiresAt": "2026-08-14T12:10:00Z",
"sanitized": true
}
{
"receipt": "SCG-R03",
"normal": "metadata_chain_accepted",
"denied": "private_destination",
"redirectsChecked": 1,
"networkResponseBodyStored": false
}
The denied path changes the token endpoint to a link-local address; no connection may be attempted. Recovery corrects the fixture metadata and starts a fresh bounded resolution rather than trusting the failed cache entry. A public-status request remains the unaffected control. Cleanup evicts only the exercise cache key.
Score one destination decision
Given a normalized URL, resolved addresses, redirect count, and environment policy, decide only allow or SSRF-deny. A public HTTPS endpoint with all public resolved addresses is allowed; any private or link-local result is denied. The expected evidence is the decision plus one policy reason.
The misconception is “HTTPS means public.” Replay an HTTPS hostname whose resolved address is private to show why scheme validation alone is insufficient. Decline tasks asking for DNS-client implementation, arbitrary proxy configuration, or complete authorization because they add multiple unknowns.
Carry sanitized metadata forward
SCG-R03 contains the canonical resource, selected issuer, sanitized endpoints, cache policy version, normal resolution, SSRF denial, and cleanup result. Day 04 uses those exact endpoints to bind an authorization request to a browser callback without turning discovery into authority.