OAuth discovery rejects cross-origin resource_metadata from WWW-Authenticate, breaking centralized PRM (SEP-985) deployments
Maintainer antworten meist innerhalb von 3 Tagen
Bewertung
- Schwierigkeit
- 3/5
- Geschätzter Aufwand
- 1-2 Tage
- Anfängerfreundlichkeit
- 74/100
- Issue-Typ
- Bug
- Klarheit
- Klar beschrieben
- Aktivitätsstatus
- Aktiv
- Tech-Stack
- rust
- Bereich
- api, authentication, security
Rechercherichtung
Start in crates/rmcp/src/transport/auth.rs at AuthorizationManager::resolve_resource_metadata_url, then trace discover_metadata and the OAuthState discovery flow. Reproduce the three-server cross-origin topology described in the issue and inspect the existing is_disallowed_metadata_host handling. Done means a permitted cross-origin resource_metadata URL is fetched, its authorization_servers value is used, and disallowed metadata hosts remain blocked.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Describe the bug
AuthorizationManager::resolve_resource_metadata_url (crates/rmcp/src/transport/auth.rs) unconditionally discards a resource_metadata URL from a WWW-Authenticate challenge when it is not same-origin (scheme+host+port) with the MCP resource:
https://github.com/modelcontextprotocol/rust-sdk/blob/main/crates/rmcp/src/transport/auth.rs
fn resolve_resource_metadata_url(value: &str, base_url: &Url) -> Option<Url> {
...
if Self::is_same_origin_resource_metadata_url(base_url, &url) {
Some(url)
} else {
warn!("rejecting resource metadata URL `{url}` because it is not same-origin with `{base_url}`");
None
}
}
This check was introduced in #935 (SSRF hardening) and is still present, unchanged and with no opt-out, on main today.
SEP-985 (tracked in #517, closed as implemented) explicitly asks the SDK to support protected-resource-metadata discovery via the resource_metadata parameter in the WWW-Authenticate challenge. That parameter is designed to point at a centralized protected-resource-metadata (PRM) endpoint, which is commonly on a different origin than the MCP resource itself — one PRM host serving metadata for many resource servers, each of which then names its own (also possibly different-origin) authorization server. The same-origin check added in #935 rejects exactly this shape unconditionally, before the authorization_servers value is ever read. There is currently no way to opt into accepting a cross-origin resource_metadata pointer — no builder method, no flag, nothing in AuthorizationManager or OAuthState addresses this specific check.
To Reproduce
Steps to reproduce the behavior:
- Stand up a resource server that returns
401withWWW-Authenticate: Bearer resource_metadata="<url on a different origin than the resource>". - Have that URL serve valid protected-resource metadata naming an
authorization_serversentry (on any origin). - Call
AuthorizationManager::discover_metadata()(or the equivalentOAuthStatediscovery flow) against the resource. - Observe that
resolve_resource_metadata_urllogsrejecting resource metadata URL ... because it is not same-originand discovery falls through without ever fetching the PRM document or the authorization server metadata.
A self-contained, dependency-free Python reproducer using three loopback-only mock servers (resource / centralized PRM / authorization server) that mirrors this exact topology is available here: https://github.com/openai/codex/issues/42427#issuecomment-5519770828
A real-world case with no mocks: https://api.anthropic.com/v1/design/mcp advertises resource_metadata at https://api.anthropic.com/v1/design/.well-known/oauth-protected-resource, which in turn names https://claude.ai/v1/design/mcp as the authorization server — a different origin from the MCP resource.
Expected behavior
The resource_metadata URL should be followed regardless of origin (subject to the same SSRF allowlist/denylist already applied to authorization-server metadata URLs, e.g. is_disallowed_metadata_host), so that a legitimate centralized PRM host — the deployment shape SEP-985 was written to support — resolves its authorization_servers and completes discovery, instead of being discarded unconditionally.
Logs
[rmcp] warn: rejecting resource metadata URL `https://<prm-host>/.well-known/oauth-protected-resource` because it is not same-origin with `https://<resource-host>/mcp`
Downstream effect in a consuming client (openai/codex), also blocked by this: https://github.com/openai/codex/issues/42427
Additional context
- This is blocking OAuth login in
openai/codex, which uses this SDK for its MCP OAuth client. Claude Code and Qwen Code both complete OAuth against theapi.anthropic.com/claude.aiexample above; Codex cannot, purely because this SDK never reaches the point of readingauthorization_servers. - Suggested fix: rather than a binary same-origin requirement on the
resource_metadataURL itself, apply the same SSRF allowlist/denylist already used for authorization-server metadata URLs (is_disallowed_metadata_host) to the resolvedresource_metadataURL as well. That preserves the SSRF protection #935 was added for while allowing the legitimate cross-origin PRM case.
- Vorherrschende Sprache
- Rust
- Sterne
- 4k
- Forks
- 654
- Ø Merge
- 4 T. 3 Std.
- Gemergte PRs (30 T.)
- 40
Entwicklungsumgebung
Startet den Dev-Container des Projekts im Browser, mit Ihrem eigenen GitHub-Konto.
- Kein Dockerfile und keine Docker-Compose-Datei
- Keine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus modelcontextprotocol/rust-sdk
-
P3 question T-documentation T-enhancement
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 86/100
modelcontextprotocol/rust-sdk#1155 ·
Maintainer antworten meist innerhalb von 3 Tagen
-
LocalSessionManager session workers don't cancel in-flight tool calls on client disconnect (follow-up to #857)Evtl. vergeben @kkkhs hat das heute übernommen. Offenbug P1 ready for work T-transport
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 55/100
modelcontextprotocol/rust-sdk#1325 ·
Maintainer antworten meist innerhalb von 3 Tagen
-
streamable-http server: client responses to server-initiated requests (sampling/elicitation/roots) are 202-accepted and silently discarded under the 2026-07-28 protocol — the pending request hangs foreverEvtl. vergeben @DaleSeo hat das vor 1 Tag übernommen. Offenbug P1 ready for work T-transport
Schwierigkeit 5/5 Über eine Woche Anfängerfreundlichkeit 35/100
modelcontextprotocol/rust-sdk#1321 · 1 Reaktion · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 3 Tagen
-
Bound pre-lifecycle bootstrap attempts in server initializationEvtl. vergeben @DaleSeo hat das vor 5 Tagen übernommen. Offenenhancement P2 T-service T-transport
modelcontextprotocol/rust-sdk#1315 · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 3 Tagen
-
ProgressDispatcher: a slow progress subscriber blocks subscribe() and delivery for other tokensEvtl. vergeben @monody0007 hat das vor 9 Tagen übernommen. Offenbug P1 ready for work T-handler
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 25/100
modelcontextprotocol/rust-sdk#1312 ·
Maintainer antworten meist innerhalb von 3 Tagen
Alle Issues in modelcontextprotocol/rust-sdk
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
bmander/geomsolver#118 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Three Windows builds are keyed on a later release than their layoutEvtl. vergeben @ero-qt hat das heute übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 2 Tagen
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 95/100
Maintainer antworten meist innerhalb von 1 Tag
-
Markdown Preview Fonts Don't Show Selected OptionEvtl. vergeben @RadhiRasho hat das heute übernommen. Offenstate:needs triage
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 61/100
zed-industries/zed#65300 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 73/100
Maintainer antworten meist innerhalb von 1 Tag