Skip to content

feat: relax StrictOidcDiscoveryMetadataPolicy and add Dynamic Client Registration middleware (RFC 7591)#269

Open
simonchrz wants to merge 4 commits intomodelcontextprotocol:mainfrom
simonchrz:main
Open

feat: relax StrictOidcDiscoveryMetadataPolicy and add Dynamic Client Registration middleware (RFC 7591)#269
simonchrz wants to merge 4 commits intomodelcontextprotocol:mainfrom
simonchrz:main

Conversation

@simonchrz
Copy link
Contributor

Add two features to the HTTP transport layer:

  1. LenientOidcDiscoveryMetadataPolicy — an alternative OIDC discovery metadata validation policy that does not require
    code_challenge_methods_supported
  2. ClientRegistrationMiddleware — PSR-15 middleware implementing OAuth 2.0 Dynamic Client Registration (RFC 7591)

Motivation and Context

LenientOidcDiscoveryMetadataPolicy: Several identity providers (FusionAuth, Microsoft Entra ID) omit code_challenge_methods_supported from
their OIDC discovery response despite fully supporting PKCE with S256. The existing OidcDiscoveryMetadataPolicy rejects these responses,
making it impossible to use the SDK's OAuth transport with those providers without a custom policy. This provides a ready-made workaround.

ClientRegistrationMiddleware: The MCP specification requires servers to support OAuth 2.0 Dynamic Client Registration (RFC 7591) so that MCP
clients can register themselves automatically. This middleware handles POST /register by delegating to a ClientRegistrarInterface
implementation and enriches /.well-known/oauth-authorization-server responses with the registration_endpoint. The ClientRegistrarInterface
keeps the actual registration logic (e.g. calling FusionAuth, storing in a database) pluggable.

How Has This Been Tested?

  • Unit tests: 16 tests covering both features
    • LenientOidcDiscoveryMetadataPolicyTest (7 cases via data providers): valid metadata with/without code_challenge_methods_supported, missing
      required fields, empty strings, non-array input
    • ClientRegistrationMiddlewareTest (9 cases): successful registration (201), invalid JSON (400), registrar exception (400), metadata
      enrichment with registration_endpoint, Cache-Control preservation, non-200 passthrough, non-matching route passthrough, empty localBaseUrl
      rejection, trailing slash normalization
  • Tested in a real Symfony application against FusionAuth as the identity provider (the motivation for these changes)

Breaking Changes

None. Both features are purely additive — new classes and interfaces only.

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 https://modelcontextprotocol.io
  • 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

Additional context

  • ClientRegistrarInterface::register() accepts and returns array<string, mixed> to stay flexible with RFC 7591's extensible metadata fields
  • ClientRegistrationMiddleware follows the same patterns as OAuthProxyMiddleware (PSR-17 factory discovery, optional DI overrides, anonymous
    handler classes in tests)
  • The registration path (/register) is extracted to a class constant for consistency
  • The middleware rewinds the response stream before reading in enrichAuthServerMetadata to handle cases where the stream cursor was already
    advanced

…allenge_methods_supported

Some identity providers (e.g. FusionAuth, Microsoft Entra ID) omit
code_challenge_methods_supported from their OIDC discovery response
despite supporting PKCE with S256. This policy relaxes the validation
to only require authorization_endpoint, token_endpoint, and jwks_uri.
PSR-15 middleware that handles POST /register by delegating to a
ClientRegistrarInterface and enriches /.well-known/oauth-authorization-server
responses with the registration_endpoint.
@sveneld
Copy link
Contributor

sveneld commented Mar 17, 2026

There is an examples for microsoft connection. There is a trouble not only with OIDC discovery.

https://github.com/modelcontextprotocol/php-sdk/blob/main/examples/server/oauth-microsoft/MicrosoftOidcMetadataPolicy.php
https://github.com/modelcontextprotocol/php-sdk/blob/main/examples/server/oauth-microsoft/MicrosoftJwtTokenValidator.php

Examples should be also updated if alternative OIDC discovery is accepted.

There is also alternative way - in case when code_challenge_methods_supported is missed we can set it's value to default S256

- Use Mcp\Exception\InvalidArgumentException instead of global \InvalidArgumentException
- Add interface documentation for ClientRegistrarInterface with RFC 7591 references
…tead

- StrictOidcDiscoveryMetadataPolicy now accepts missing code_challenge_methods_supported (defaults to S256 downstream)
- If the field is present, it is still validated strictly
- Remove LenientOidcDiscoveryMetadataPolicy (no longer needed)
- Update tests and CHANGELOG accordingly
@simonchrz
Copy link
Contributor Author

There is an examples for microsoft connection. There is a trouble not only with OIDC discovery.

https://github.com/modelcontextprotocol/php-sdk/blob/main/examples/server/oauth-microsoft/MicrosoftOidcMetadataPolicy.php https://github.com/modelcontextprotocol/php-sdk/blob/main/examples/server/oauth-microsoft/MicrosoftJwtTokenValidator.php

Examples should be also updated if alternative OIDC discovery is accepted.

There is also alternative way - in case when code_challenge_methods_supported is missed we can set it's value to default S256

Good point — I've removed LenientOidcDiscoveryMetadataPolicy and relaxed StrictOidcDiscoveryMetadataPolicy to accept missing code_challenge_methods_supported. If the field is present it's still validated strictly; if absent, the downstream OAuthProxyMiddleware already defaults to ['S256']. The interface stays for custom policies (like the Microsoft example).

@simonchrz simonchrz closed this Mar 18, 2026
@simonchrz simonchrz reopened this Mar 18, 2026
@simonchrz simonchrz changed the title feat: add LenientOidcDiscoveryMetadataPolicy and Dynamic Client Registration middleware (RFC 7591) feat: relax StrictOidcDiscoveryMetadataPolicy and add Dynamic Client Registration middleware (RFC 7591) Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants