Skip to content

feat(auth): add unified auth protocol discovery and optional DPoP support#2003

Open
nypdmax wants to merge 60 commits intomodelcontextprotocol:mainfrom
nypdmax:pr/auth-ext-cherrypick
Open

feat(auth): add unified auth protocol discovery and optional DPoP support#2003
nypdmax wants to merge 60 commits intomodelcontextprotocol:mainfrom
nypdmax:pr/auth-ext-cherrypick

Conversation

@nypdmax
Copy link

@nypdmax nypdmax commented Feb 6, 2026

Motivation and Context

This PR adds multi-protocol authentication support to the MCP Python SDK, centered around unified authorization
server discovery
so clients can discover, select, and apply one of multiple supported auth protocols consistently.

Key capabilities:

  • Unified auth protocol discovery via /.well-known/authorization_servers plus RFC 9728 PRM fallback.
    • Allows servers to advertise multiple protocols (e.g. oauth2, api_key) with optional default protocol and
      preferences.
    • Allows clients to select an injected protocol instance based on server hints and preferences, with safe fallbacks.
  • Extensible protocol metadata (MCP extension fields) to represent protocol endpoints/capabilities consistently.
  • DPoP support (RFC 9449):
    • Client-side proof generation and header injection (opt-in).
    • Server-side proof verification integrated into verification flow (opt-in).
  • Streamable HTTP server maintainability: refactors request handling to reduce complexity while preserving behavior.
  • Examples support: adds a small FastMCP compatibility wrapper used by examples and updates multiprotocol examples.
  • Documentation updates covering protocol discovery and multiprotocol usage.

How Has This Been Tested?

  • Full test suite:
    • PYTEST_DISABLE_PLUGIN_AUTOLOAD="" uv run --frozen pytest
    • Result: 1229 passed, 95 skipped, 1 xfailed
  • Lint / typecheck:
    • uv run --frozen ruff format --check .
    • uv run --frozen ruff check .
    • uv run --frozen pyright

Spec compliance notes (additive extensions)

This PR keeps MCP Authorization (2025-06-18) OAuth behavior intact (RFC 9728 PRM + RFC 8414 metadata + RFC 8707
resource indicators). It also adds optional extensions to support multi-protocol auth and stronger token binding:

  • Multi-protocol selection (extension):

    • Optional /.well-known/authorization_servers endpoint (best-effort; client falls back if missing).
    • Optional PRM extension fields: mcp_auth_protocols, mcp_default_auth_protocol, mcp_auth_protocol_preferences.
    • Optional WWW-Authenticate: Bearer ... hint params: auth_protocols, default_protocol, protocol_preferences
      (ignored by spec-only clients).
  • DPoP (RFC 9449) (optional extension): adds opt-in DPoP proof generation/verification; standard Bearer flow remains
    supported when disabled.

Interoperability: spec-only servers/clients can ignore these extensions and continue using the standard OAuth discovery
and Bearer token flow.

Breaking Changes

None expected. Changes are additive and aim to preserve existing behavior and public APIs.

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

Additional context

Suggested review order (commit-by-commit):

  1. Streamable HTTP server refactor
  2. Auth server token handling + verifiers (and tests)
  3. Multi-protocol client/provider behavior + test coverage
  4. DPoP client/server pieces + tests
  5. FastMCP wrapper + updated examples
  6. Documentation updates

…ig for examples/clients

- Add examples/clients/simple-auth-multiprotocol-client (API Key + mTLS placeholder)
- Pyright: include examples/clients; executionEnvironments + extraPaths for client packages
- Fixes import resolution for mcp_simple_auth_multiprotocol_client.main in __main__.py
…rotocol integration test script

- APIKeyVerifier: optional scopes param for AccessToken (satisfy required_scopes)
- simple-auth-multiprotocol: build_multiprotocol_backend(api_key_scopes=[mcp_scope])
- scripts/run_phase2_multiprotocol_integration_test.sh: api_key (default), oauth (AS+RS+simple-auth-client), mutual_tls (placeholder)
…erator and storage

- Add DPoPKeyPair for ES256/RS256 key generation with configurable RSA key size
- Add DPoPProofGeneratorImpl for generating DPoP proof JWTs per RFC 9449
- Add InMemoryDPoPStorage for key pair persistence
- Add compute_jwk_thumbprint for RFC 7638 JWK Thumbprint calculation
- Include comprehensive unit tests (12 test cases)
Add DPoPProofVerifier for validating DPoP proof JWTs per RFC 9449 Section 4.3,
including replay protection, claim validation, and JWK thumbprint verification.
Add DPoP support to OAuth2Protocol implementing DPoPEnabledProtocol,
inject DPoP proofs via MultiProtocolAuthProvider, and verify DPoP-bound
tokens in OAuthTokenVerifier.
Add --dpop-enabled CLI flag and integrate DPoPProofVerifier with
MultiProtocolAuthBackend for end-to-end DPoP verification.
- Add scripts/run_phase4_dpop_integration_test.sh for automated DPoP tests
- Update test_oauth2_protocol: expect OAuthFlowError when http_client is None
- Update simple-auth-multiprotocol-client: OAuth+DPoP support, InMemoryStorage
- Update simple-auth-multiprotocol: DPoP logging, oauth2 protocol_version 2.0
- Add _oauth_401_flow.py: oauth_401_flow_generator, oauth_403_flow_generator
- Refactor OAuthClientProvider.async_auth_flow to drive shared generators
- Refactor MultiProtocolAuthProvider 401 handling to use shared OAuth flow
- Fix OAuth2Protocol.authenticate to use fresh client when called outside flow
- Fix OAuthTokenVerifier: use scope for HTTP method (HTTPConnection compat)
Prefer only locally injected protocol instances when selecting the auth protocol, and ensure the final response corresponds to the original request (avoid leaking discovery responses).
Add regression tests ensuring 401 handling falls back when the server default protocol isn't injected, and that discovery responses are never returned as the business request response.
Make api_key and mutual_tls runs non-interactive with clear PASS/FAIL, add oauth_dpop mode wiring, and allow forcing mutual_tls protocol injection for placeholder coverage.
Ignore /plans/ and /.cursorrules to keep local scratch docs and Cursor config out of version control.
- OAuthClientProvider: add fixed_client_info, _exchange_token_client_credentials
- OAuth2Protocol: accept fixed_client_info for M2M flows
- MultiProtocolAuthProvider: pass fixed_client_info into OAuthClientProvider
- _oauth_401_flow: require client_info for client_credentials, skip dynamic registration
- Server token handler: ClientCredentialsRequest, exchange_client_credentials
- Server routes: advertise client_credentials in grant_types_supported
…overy variants

- simple-auth: exchange_client_credentials, demo client_credentials client
- simple-auth-multiprotocol: add prm_only, path_only, root_only, oauth_fallback
  server variants and CLI entry points for discovery E2E testing
Also sync grant_types_supported test assertions with 6cf977b.
- authorization-multiprotocol.md: discovery order (PRM → path-relative → root → OAuth fallback), client flow, [Auth discovery] DEBUG logs
- auth-analysis.md: multi-protocol discovery order and logging in §4.2, §12.1.3, §13.2.2
- multi-protocol-refactoring-plan.md: §11.4 PRM-first order, §11.5 note, §3.2.2 pseudo-code; auth discovery logging
Update multiprotocol docs/READMEs to use MCP_AUTH_PROTOCOL (no PHASE wording) and align example instructions with the hardened scripts.
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