Skip to content

fix(auth): allow cross-origin resource_metadata urls - #1302

Merged
alexhancock merged 3 commits into
mainfrom
DaleSeo/oauth-discovery-rejects-cross-origin-resource_me
Oct 22, 2026
Merged

alexhancock merged 3 commits into
mainfrom
DaleSeo/oauth-discovery-rejects-cross-origin-resource_me

Conversation

@DaleSeo

@DaleSeo DaleSeo commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Fixes #1298.

Motivation and Context

#935 added a same-origin requirement for the challenge's resource_metadata URL to block SSRF. But that also rejects a valid setup where one central protected resource metadata (PRM) host serves metadata for multiple resource servers, which RFC 9728 and SEP-985 allow. Discovery discarded the pointer before reading authorization_servers.

Now, a resource_metadata URL from a WWW-Authenticate challenge doesn't have to be on the same origin as the MCP resource. Instead, it goes through the same SSRF host check used for authorization server URLs.

How Has This Been Tested?

Add tests

Breaking Changes

None.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

@github-actions github-actions Bot added T-core Core library changes T-transport Transport layer changes labels Sep 26, 2026
@DaleSeo
DaleSeo marked this pull request as ready for review September 26, 2026 16:37
@DaleSeo
DaleSeo requested a review from a team as a code owner September 26, 2026 16:37
@DaleSeo DaleSeo self-assigned this Sep 28, 2026
@DaleSeo
DaleSeo force-pushed the DaleSeo/oauth-discovery-rejects-cross-origin-resource_me branch from 29e2280 to 412011b Compare September 28, 2026 15:28
Comment thread crates/rmcp/src/transport/auth.rs Outdated
}

fn is_allowed_authorization_server_metadata_url(base_url: &Url, url: &Url) -> bool {
fn is_allowed_metadata_url(base_url: &Url, url: &Url) -> bool {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't appear to me to check if it's http vs https.

Self::is_same_origin(base_url, candidate) used to do this incidentally by comparing the schemes, but now nothing does.

Should consider adding an explicit check for that back to this method.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexhancock Good catch, thanks. Added an explicit no-downgrade check.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same-origin also provided a second property incidentally, beyond the scheme: it did not depend on DNS. is_allowed_metadata_url still decides from the URL's host string, and for a DNS name that returns "allowed" without constraining the address the client connects to. With same-origin gone, that now applies to resource_metadata as well. I sent the details through the advisory process (GHSA-5p52-q7pq-9fq2) and suggested a fix in #1298: filter resolved addresses with a reqwest::dns::Resolve on both discovery clients. Worth settling before this merges.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging, @ndreno. Would you be willing to start a temporary private fork to follow the advisory process? I'd like to keep this PR focused on cross-origin metadata support and hold off on merging until the security fix is ready and the maintainers have agreed on sequencing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll start the fork from the advisory and put the resolver-level fix there, with the reproduction turned into regression tests. One design question will come with it, on how the loopback exception carries over once the check moves to resolution time. I'll raise it in the fork.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋 thanks @ndreno and @DaleSeo. Is the security fix still waiting on review or a decision on loopback handling? What’s left to unblock it? We also have a few issues waiting to be unblocked by this change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for checking in, @mzeng-openai. This PR is still waiting on the related security patch in the advisory's private fork. I reviewed the fix and left some feedback, but I haven't heard back yet.

@ndreno, could you take a look when you get a chance? If you don't have time right now, just let me know. The maintainers can take it from here so this doesn't hold anyone up.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry guys, I'm quite overwhelmed with work those days and won't be able to look at those topics for some time

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no worries, can we have someone else take on the security patch in the meanwhile?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mzeng-openai I took over the security patch, and it's now merged into main. I've rebased this PR on top of it, so it's ready for review again. Both will be included in the next release, 3.7.0. Thanks for your patience!

@DaleSeo
DaleSeo force-pushed the DaleSeo/oauth-discovery-rejects-cross-origin-resource_me branch from 412011b to d97b571 Compare September 28, 2026 21:51
@DaleSeo
DaleSeo requested a review from alexhancock September 28, 2026 21:52
alexhancock
alexhancock previously approved these changes Sep 29, 2026
@DaleSeo
DaleSeo marked this pull request as draft September 29, 2026 22:04
@DaleSeo
DaleSeo force-pushed the DaleSeo/oauth-discovery-rejects-cross-origin-resource_me branch from d97b571 to 045fff0 Compare October 22, 2026 13:21
@DaleSeo
DaleSeo marked this pull request as ready for review October 22, 2026 13:23
@DaleSeo
DaleSeo requested a review from alexhancock October 22, 2026 13:25
@DaleSeo
DaleSeo force-pushed the DaleSeo/oauth-discovery-rejects-cross-origin-resource_me branch from 6727af1 to 526e2b2 Compare October 22, 2026 13:45
Routing every resource_metadata URL through the metadata host check rejected a resource's own origin when it is a private address, such as https://10.0.0.1/mcp pointing at /custom/prm.json, and new_allowing_private_addresses couldn't override it. Same-origin pointers are accepted again, cross-origin pointers follow the manager's private-address policy on every path, including resolve_metadata_from_challenge, and rejected resource_metadata URLs log the reason.
@DaleSeo
DaleSeo force-pushed the DaleSeo/oauth-discovery-rejects-cross-origin-resource_me branch from 526e2b2 to 4183f89 Compare October 22, 2026 13:47
@alexhancock
alexhancock merged commit 7bc5c07 into main Oct 22, 2026
24 checks passed
@alexhancock
alexhancock deleted the DaleSeo/oauth-discovery-rejects-cross-origin-resource_me branch October 22, 2026 14:41
@github-actions github-actions Bot mentioned this pull request Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-transport Transport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OAuth discovery rejects cross-origin resource_metadata from WWW-Authenticate, breaking centralized PRM (SEP-985) deployments

4 participants