Skip to content

[Client] Add OAuth metadata discovery and challenge parsing - #664

Open
chr-hertel wants to merge 10 commits into
modelcontextprotocol:mainfrom
chr-hertel:client-auth-discovery
Open

chr-hertel wants to merge 10 commits into
modelcontextprotocol:mainfrom
chr-hertel:client-auth-discovery

Conversation

@chr-hertel

Copy link
Copy Markdown
Member

Part of #317 & #318 - second slice of the client authorization in chr-hertel#53, independent of #663.

Finds and validates the documents that say who guards an MCP server: protected resource metadata (RFC 9728), authorization server metadata (RFC 8414 & OpenID Connect) and the WWW-Authenticate challenge that points at them. These documents come from a server we don't trust, so:

  • endpoints need https or a loopback host, with the same SecureUrl check the server side uses
  • a document naming another issuer than it was fetched for is discarded
  • covers() & isWithin() answer whether a token for a resource may go to a URL

All @internal and not used yet, the authenticator comes with the next slice - hence no changelog entry or docs. Whether the challenge's resource_metadata URL can be trusted is also decided there, MetadataDiscovery just fetches what it gets.

@chr-hertel chr-hertel added Client Issues & PRs related to the Client component auth Issues and PRs related to Authentication / OAuth labels Nov 4, 2026
@chr-hertel
chr-hertel requested a balanced review from Copilot November 4, 2026 22:06

Copilot AI left a comment

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.

🟡 Changes recommended

Resource containment ignores query constraints and accepts dot-segment path escapes, potentially allowing resource-scoped tokens to be sent to the wrong tenant or an out-of-scope endpoint.

7 open issues
What changed in this PR

Adds internal OAuth discovery and challenge-parsing helpers for the PHP client's upcoming authenticator, without changing transport behavior yet.

Changes:

  • Adds protected-resource and authorization-server metadata discovery, including OpenID Connect fallback.
  • Introduces metadata models with endpoint validation, issuer matching, and resource-containment checks.
  • Parses Bearer challenges and adds unit coverage for discovery, validation, and parsing.
File Description
tests/​Unit/​Client/​Auth/​ProtectedResourceMetadataTest.php Tests resource containment and metadata parsing.
tests/​Unit/​Client/​Auth/​MetadataDiscoveryTest.php Tests discovery locations, fallback, and issuer rejection.
tests/​Unit/​Client/​Auth/​AuthorizationServerMetadataTest.php Tests endpoint validation, capabilities, and legacy defaults.
tests/​Unit/​Client/​Auth/​AuthorizationChallengeTest.php Tests Bearer parameters and quoted-value parsing.
src/​Client/​Auth/​ProtectedResourceMetadata.php Models protected resources and checks URL containment.
src/​Client/​Auth/​MetadataDiscovery.php Fetches metadata from ordered discovery locations.
src/​Client/​Auth/​AuthorizationServerMetadata.php Models authorization metadata and validates endpoint security.
src/​Client/​Auth/​AuthorizationChallenge.php Extracts metadata URLs, scopes, and errors from challenges.

Review effort: Balanced

Comment thread src/Client/Auth/MetadataDiscovery.php Resolved
Finds and validates the documents that say who guards an MCP server: protected
resource metadata (RFC 9728) and authorization server metadata (RFC 8414 and
OpenID Connect), plus the WWW-Authenticate challenge that points at them.

All of it is @internal, since nothing uses it until the authenticator lands.
Endpoints have to pass SecureUrl, and a document naming another issuer than it
was fetched for is discarded.

Copilot AI left a comment

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.

Comment thread src/Client/Auth/MetadataDiscovery.php Resolved

Copilot AI left a comment

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.

🔵 Needs a closer look

Discovery can accept unrelated resource metadata, alter meaningful issuer paths, and skip valid fallbacks, while split authentication headers can cause metadata URLs to be missed.

No open issues 🎉

4 previously missed issues

In code that hasn't changed since last review

Medium severity Parse WWW-Authenticate as a combined header value

src/​Client/​Auth/​AuthorizationChallenge.php:42

A single Bearer challenge can span multiple WWW-Authenticate field lines, which HTTP treats as one comma-joined value. For example, splitting Bearer scope="read" and resource_metadata="https://mcp.example.com/meta" across two lines makes this method return before reading the metadata URL; parse getHeaderLine('WWW-Authenticate') once so equivalent split and combined headers produce the same result.

Medium severity Skip metadata that does not cover the requested resource

src/​Client/​Auth/​MetadataDiscovery.php:53

A document naming an unrelated resource is returned without checking whether it covers $resourceUrl, so its authorization servers are accepted and a valid host-root fallback is never tried. Check covers($resourceUrl) before returning and skip documents whose resource does not cover the requested endpoint.

Medium severity Continue discovery after an issuer-mismatched candidate

src/​Client/​Auth/​MetadataDiscovery.php:97

If the RFC 8414 location returns another issuer's metadata but a later OIDC location contains the correct document, this return aborts discovery before that valid fallback is requested. Discard this candidate with continue, as for other malformed documents, while retaining the exact issuer check on each subsequent candidate.

Medium severity Preserve repeated leading slashes in issuer paths

src/​Client/​Auth/​MetadataDiscovery.php:136

trim() removes meaningful leading slashes from the issuer's path: for https://auth.example.com//tenant, all three candidates use /tenant instead of //tenant and therefore probe the wrong locations. Preserve additional leading slashes when removing the initial path separator so discovery also works for these valid issuer URLs.

3 resolved since last review

Review effort: Balanced

Copilot AI left a comment

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.

🔵 Needs a closer look

Human assessment is needed because trailing-slash normalization can extend resource-scoped token access beyond its intended subtree, creating a critical authorization-boundary risk.

No open issues 🎉

Review effort: Balanced

Copilot AI left a comment

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.

🔵 Needs a closer look

Human assessment is needed to evaluate a critical legacy-discovery bypass of transport-security checks that can expose credentials over HTTP.

No open issues 🎉

Review effort: Balanced

Copilot AI left a comment

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.

🔵 Needs a closer look

Human assessment is needed because metadata validation can approve insecure token recipients, while discovery can accept mismatched resources and derive incorrect authorization endpoints.

No open issues 🎉

Review effort: Balanced

Copilot AI left a comment

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.

🔵 Needs a closer look

Human assessment is needed because metadata discovery can accept a document for an unrelated resource and stop fallback before reaching valid metadata.

No open issues 🎉

Review effort: Balanced

@chr-hertel

Copy link
Copy Markdown
Member Author

Human assessment is needed because metadata discovery can accept a document for an unrelated resource and stop fallback before reaching valid metadata.

off scope here

@chr-hertel chr-hertel added this to the 1.0.0 milestone Nov 5, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auth Issues and PRs related to Authentication / OAuth Client Issues & PRs related to the Client component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants