Skip to content

[Server] Add native OAuth 2.1 authorization server#373

Draft
wachterjohannes wants to merge 1 commit into
modelcontextprotocol:mainfrom
wachterjohannes:feature/oauth-authorization-server
Draft

[Server] Add native OAuth 2.1 authorization server#373
wachterjohannes wants to merge 1 commit into
modelcontextprotocol:mainfrom
wachterjohannes:feature/oauth-authorization-server

Conversation

@wachterjohannes
Copy link
Copy Markdown
Contributor

@wachterjohannes wachterjohannes commented Jun 2, 2026

Summary

The HTTP server transport already supports being an OAuth resource server (validating tokens) and an OAuth proxy to an upstream IdP. This adds the missing third piece: a self-contained OAuth 2.1 authorization server, so an MCP server can register clients, authorize users, and issue + validate its own RS256 JWT access tokens — without an external IdP or a third-party OAuth library (e.g. league/oauth2-server).

It is dependency-free apart from firebase/php-jwt, which the existing JwtTokenValidator already uses for validation; signing reuses it. No new hard dependency.

What's included (src/Server/Transport/Http/)

  • Engine behind seamsAuthorizationCodeIssuerInterface / TokenGranterInterface with Native* implementations: authorization code grant with mandatory PKCE (S256), refresh-token rotation, one-time short-TTL codes, exact redirect_uri matching, constant-time client auth.
  • Tokens & keysJwtAccessTokenIssuer (RS256) producing tokens that validate unchanged through the existing JwtTokenValidator; RsaSigningKey, JwksMiddleware, and StaticJwksProvider for in-process self-validation (no network round-trip).
  • EndpointsAuthorizationEndpointMiddleware, TokenEndpointMiddleware, AuthorizationServerMetadataMiddleware (RFC 8414); DCR (RFC 7591) via StoredClientRegistrar + the existing ClientRegistrationMiddleware.
  • StorageClientRepositoryInterface, AuthorizationCodeStoreInterface, RefreshTokenStoreInterface, optional AccessTokenStoreInterface, each with in-memory and PSR-16 implementations.
  • Host seamsResourceOwnerResolverInterface (the SDK can't authenticate users) and ConsentInterface (+ default AutoApproveConsent).
  • OAuthException for RFC 6749 §5.2 error responses.
  • A runnable example at examples/server/oauth-authorization-server/.

The engine is transport-agnostic (the middlewares are thin PSR-7 shells over it), so it can be driven directly from a framework controller too.

Tests

144 new unit tests (PKCE RFC 7636 vector, self-issued round-trip through JwtTokenValidator, granter behaviours, registrar, metadata, middlewares). phpstan level 6 clean.

Status

Draft. This is the SDK half of a larger effort to let Symfony MCP servers act as their own OAuth AS. Companion work, built on these primitives and validated end-to-end against a real Sulu MCP server:

Feedback on the public API/seams welcome.

Adds a self-contained OAuth 2.1 authorization-server layer to the HTTP
server transport, complementing the existing resource-server/proxy support
so an MCP server can issue and validate its own access tokens without an
external IdP or a third-party OAuth library.

- Engine behind interfaces (AuthorizationCodeIssuerInterface,
  TokenGranterInterface) with native implementations: authorization code
  grant with mandatory PKCE S256, refresh-token rotation, one-time codes.
- RS256 JWT access tokens (JwtAccessTokenIssuer) that validate through the
  existing JwtTokenValidator; RsaSigningKey + JWKS publishing; StaticJwksProvider
  for in-process self-validation.
- Storage interfaces (clients, authorization codes, refresh tokens, optional
  access tokens) with in-memory and PSR-16 implementations.
- RFC 8414 AuthorizationServerMetadata, RFC 7591 StoredClientRegistrar, and
  the AuthorizationEndpoint/Token/AuthorizationServerMetadata/Jwks middlewares.
- Host seams: ResourceOwnerResolverInterface and ConsentInterface (+ default
  AutoApproveConsent).
- OAuthException for RFC 6749 error responses, plus a runnable example.

Signing uses firebase/php-jwt (already used by JwtTokenValidator); no new
hard dependency.
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.

1 participant