Skip to content

feat: support protected resource metadata for mcp server #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

xiaoyijun
Copy link
Contributor

This pull request introduces a significant architectural enhancement to the mcp-auth Python library, refactoring it to support modern OAuth 2.0 Protected Resource Metadata (RFC 9728) while maintaining full backward compatibility with the existing Authorization Server mode. This change enables developers to configure multiple distinct resources, each with its own set of trusted authorization servers, within a single MCPAuth instance.

Core Architectural Changes

The refactor is centered around a clearer, more scalable internal architecture, leveraging the Strategy design pattern:

  • Dual-Mode Configuration with Strategy Handlers: The MCPAuth constructor now acts as a factory, instantiating a corresponding handler to manage mode-specific logic based on the provided configuration.
    • Authorization Server Mode: Implemented by AuthorizationServerHandler, this supports the legacy, single-AS configuration and is now formally deprecated.
    • Resource Server Mode: Implemented by ResourceServerHandler, this is the new, recommended approach. It accepts a single ResourceServerConfig or a list of them, allowing the definition of multiple protected resources and their associated policies.
  • TokenVerifier: An Authentication Policy Encapsulator: A new core internal class, TokenVerifier, has been introduced. This class encapsulates the complete authentication policy for a single logical resource.
    • Each TokenVerifier instance is bound to the specific list of AuthServerConfig objects that its associated resource trusts.
    • It is responsible for all authentication logic for that resource, including:
      • Creating on-demand JWT verification functions that are pre-configured with the correct jwks_uri.
      • Validating that a token's iss (issuer) claim is within its list of trusted issuers, providing precise and helpful error messages that list all expected issuers on failure.
    • This abstraction cleanly separates the policy definition from the main MCPAuth class, which now acts as a high-level factory and registry that routes requests to the correct TokenVerifier.

API Enhancements and Usage

The public-facing API has evolved to be more powerful and expressive without introducing breaking changes.

  • Smarter bearer_auth_middleware: The bearer_auth_middleware method is now significantly more powerful.
    • It accepts a new resource parameter. This property is required when MCPAuth is initialized in protected_resources mode and signals which resource's authentication policy to apply.
    • MCPAuth uses the resource identifier to look up the corresponding TokenVerifier and uses it to validate the token. This makes protecting endpoints for different resources explicit and robust.
    • For users of the legacy server configuration, the behavior of bearer_auth_middleware remains unchanged, ensuring zero breaking changes.
  • New resource_metadata_router(): A new method that creates a Starlette router to automatically serve the Protected Resource Metadata for all configured resources.
    • It correctly implements the /.well-known/oauth-protected-resource/... pathing logic as defined in RFC 9728.
    • The old metadata_route() method is now marked as @deprecated.

Example Usage

Here is a practical example of how to configure and use the new resource server mode to protect an API endpoint:

from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.routing import Mount

from mcpauth import MCPAuth
from mcpauth.config import AuthServerType, ResourceServerConfig, ResourceServerMetadata
from mcpauth.utils import fetch_server_config

# 1. Define your resource identifier and fetch the config for its trusted authorization server.
resource_id = "https://todo-manager.mcp-auth.com/resource1"
auth_server_config = fetch_server_config("https://auth.logto.io/oidc", AuthServerType.OIDC)

# 2. Initialize MCPAuth in resource server mode.
# `protected_resources` can be a single object or a list for multiple resources.
mcp_auth = MCPAuth(
    protected_resources=ResourceServerConfig(
        metadata=ResourceServerMetadata(
            resource=resource_id,
            authorization_servers=[auth_server_config],
            scopes_supported=[
                "create:todos",
                "read:todos",
                "delete:todos",
            ],
        )
    )
)

# 3. Create the middleware to protect your MCP server with the resource-specific policy.
bearer_auth = Middleware(mcp_auth.bearer_auth_middleware('jwt', resource=resource_id))

# 4. Mount the router to serve the Protected Resource Metadata.
# This will create an endpoint at `/.well-known/oauth-protected-resource/todo-manager.mcp-auth.com%2Fresource1`.
app = Starlette(
    routes=[
        *mcp_auth.resource_metadata_router().routes,
        # Protect the MCP server with the Bearer auth middleware.
        Mount("/", app=mcp.sse_app(), middleware=[bearer_auth]),
    ],
)

Copy link

codecov bot commented Jun 18, 2025

Codecov Report

Attention: Patch coverage is 99.34211% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mcpauth/auth/resource_server_handler.py 98.43% 0 Missing and 1 partial ⚠️
mcpauth/middleware/create_bearer_auth.py 97.22% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@xiaoyijun xiaoyijun force-pushed the xiaoyijun-log-11632-python-support-new-mcp-auth-spec branch from dc1f2e6 to 7ac6d17 Compare June 18, 2025 11:38
@xiaoyijun xiaoyijun force-pushed the xiaoyijun-log-11632-python-support-new-mcp-auth-spec branch 4 times, most recently from 0399400 to 7552e9d Compare June 18, 2025 12:02
@xiaoyijun xiaoyijun force-pushed the xiaoyijun-log-11632-python-support-new-mcp-auth-spec branch from 7552e9d to b3a2417 Compare June 18, 2025 12:04
@xiaoyijun xiaoyijun requested a review from gao-sun June 18, 2025 12:07
@scottefein
Copy link

This looks great! Any update on when you'll be able to merge this?

@gao-sun gao-sun requested a review from Copilot July 2, 2025 22:21
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the mcp-auth library to support OAuth 2.0 Protected Resource Metadata (RFC 9728) while preserving backward compatibility with the legacy authorization server mode. It introduces strategy-based handlers, a TokenVerifier abstraction, and enhanced middleware and routing for protected resource metadata.

  • Introduce AuthorizationServerHandler (deprecated) and ResourceServerHandler for dual-mode operation
  • Add TokenVerifier to encapsulate validation logic per resource
  • Enhance bearer_auth_middleware with a resource parameter and add resource_metadata_router()

Reviewed Changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
mcpauth/utils/_transpile_resource_metadata.py Convert MCPAuth config metadata to standard protected-resource metadata
mcpauth/utils/_create_resource_metadata_endpoint.py Build RFC 9728 metadata endpoint URLs
mcpauth/utils/_bearer_www_authenticate_header.py Implement chained header builder for WWW-Authenticate
mcpauth/middleware/create_bearer_auth.py Extend error handling and include resource_metadata header
mcpauth/types.py Add ResourceServerMetadata & ResourceServerConfig types
mcpauth/auth/token_verifier.py Encapsulate JWT issuer extraction and verification per server
mcpauth/auth/resource_server_handler.py Implement protected-resource mode routing & verifier lookup
mcpauth/auth/authorization_server_handler.py Maintain legacy auth-server mode with deprecation warning
mcpauth/init.py Initialize correct handler based on server vs protected_resources
tests/* Add and update tests for new resource metadata, header, and middleware behavior
samples/server/* Update example apps to use resource-server configuration
Comments suppressed due to low confidence (2)

mcpauth/middleware/create_bearer_auth.py:94

  • Update the _handle_error docstring to note that it now returns three values (status_code, response_body, headers) instead of two.
    """

mcpauth/middleware/create_bearer_auth.py:44

  • [nitpick] The field name resource in BearerAuthConfig can be ambiguous; consider renaming it to resource_id for consistency with other APIs and to clarify that it’s an identifier rather than the metadata itself.
    resource: Optional[str] = None

@xiaoyijun xiaoyijun merged commit a9c75d4 into master Jul 3, 2025
6 checks passed
@xiaoyijun xiaoyijun deleted the xiaoyijun-log-11632-python-support-new-mcp-auth-spec branch July 3, 2025 01:46
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