Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 30 additions & 18 deletions mcpauth/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import logging
from typing import Any, Literal, Union
from typing import List, Literal, Optional, Union

from .middleware.create_bearer_auth import BaseBearerAuthConfig, BearerAuthConfig
from .middleware.create_bearer_auth import BearerAuthConfig
from .types import VerifyAccessTokenFunction
from .config import MCPAuthConfig
from .config import AuthServerConfig
from .exceptions import MCPAuthAuthServerException, AuthServerExceptionCode
from .utils import validate_server_config
from starlette.middleware.base import BaseHTTPMiddleware
Expand All @@ -16,12 +16,14 @@ class MCPAuth:
functions for handling OAuth 2.0-related tasks and bearer token auth.

See Also: https://mcp-auth.dev for more information about the library and its usage.

:param config: An instance of `MCPAuthConfig` containing the server configuration.
"""

def __init__(self, config: MCPAuthConfig):
result = validate_server_config(config.server)
def __init__(self, server: AuthServerConfig):
"""
:param server: Configuration for the remote authorization server.
"""

result = validate_server_config(server)

if not result.is_valid:
logging.error(
Expand All @@ -37,13 +39,13 @@ def __init__(self, config: MCPAuthConfig):
for warning in result.warnings:
logging.warning(f"- {warning}")

self.config = config
self.server = server

def metadata_response(self) -> JSONResponse:
"""
Returns a response containing the server metadata in JSON format with CORS support.
"""
server_config = self.config.server
server_config = self.server

response = JSONResponse(
server_config.metadata.model_dump(exclude_none=True),
Expand All @@ -56,22 +58,26 @@ def metadata_response(self) -> JSONResponse:
def bearer_auth_middleware(
self,
mode_or_verify: Union[Literal["jwt"], VerifyAccessTokenFunction],
config: BaseBearerAuthConfig = BaseBearerAuthConfig(),
jwt_options: dict[str, Any] = {},
audience: Optional[str] = None,
required_scopes: Optional[List[str]] = None,
show_error_details: bool = False,
leeway: float = 60,
) -> type[BaseHTTPMiddleware]:
"""
Creates a middleware that handles bearer token authentication.

:param mode_or_verify: If "jwt", uses built-in JWT verification; or a custom function that
takes a string token and returns an `AuthInfo` object.
:param config: Configuration for the Bearer auth handler, including audience, required
scopes, etc.
:param jwt_options: Optional dictionary of additional options for JWT verification
(`jwt.decode`). Not used if a custom function is provided.
:param audience: Optional audience to verify against the token.
:param required_scopes: Optional list of scopes that the token must contain.
:param show_error_details: Whether to include detailed error information in the response.
Defaults to `False`.
:param leeway: Optional leeway in seconds for JWT verification (`jwt.decode`). Defaults to
`60`. Not used if a custom function is provided.
:return: A middleware class that can be used in a Starlette or FastAPI application.
"""

metadata = self.config.server.metadata
metadata = self.server.metadata
if isinstance(mode_or_verify, str) and mode_or_verify == "jwt":
from .utils import create_verify_jwt

Expand All @@ -82,7 +88,7 @@ def bearer_auth_middleware(

verify = create_verify_jwt(
metadata.jwks_uri,
options=jwt_options,
leeway=leeway,
)
elif callable(mode_or_verify):
verify = mode_or_verify
Expand All @@ -94,5 +100,11 @@ def bearer_auth_middleware(
from .middleware.create_bearer_auth import create_bearer_auth

return create_bearer_auth(
verify, BearerAuthConfig(issuer=metadata.issuer, **config.model_dump())
verify,
BearerAuthConfig(
issuer=metadata.issuer,
audience=audience,
required_scopes=required_scopes,
show_error_details=show_error_details,
),
)
144 changes: 139 additions & 5 deletions mcpauth/config.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,147 @@
from .models.auth_server import AuthServerConfig
from enum import Enum
from typing import List, Optional
from pydantic import BaseModel


class MCPAuthConfig(BaseModel):
class AuthorizationServerMetadata(BaseModel):
"""
Configuration for the `MCPAuth` class.
Pydantic model for OAuth 2.0 Authorization Server Metadata as defined in RFC 8414.
"""

server: AuthServerConfig
issuer: str
"""
Config for the remote authorization server.
The authorization server's issuer identifier, which is a URL that uses the `https` scheme and
has no query or fragment components.
"""

authorization_endpoint: str
"""
URL of the authorization server's authorization endpoint [[RFC6749](https://rfc-editor.org/rfc/rfc6749)].
This is REQUIRED unless no grant types are supported that use the authorization endpoint.

See: https://rfc-editor.org/rfc/rfc6749#section-3.1
"""

token_endpoint: str
"""
URL of the authorization server's token endpoint [[RFC6749](https://rfc-editor.org/rfc/rfc6749)].
This is REQUIRED unless only the implicit grant type is supported.

See: https://rfc-editor.org/rfc/rfc6749#section-3.2
"""

jwks_uri: Optional[str] = None
"""
URL of the authorization server's JWK Set [[JWK](https://www.rfc-editor.org/rfc/rfc8414.html#ref-JWK)] document.
The referenced document contains the signing key(s) the client uses to validate signatures
from the authorization server. This URL MUST use the `https` scheme.
"""

registration_endpoint: Optional[str] = None
"""
URL of the authorization server's OAuth 2.0 Dynamic Client Registration endpoint
[[RFC7591](https://www.rfc-editor.org/rfc/rfc7591)].
"""

scope_supported: Optional[List[str]] = None

response_types_supported: List[str]
"""
JSON array containing a list of the OAuth 2.0 `response_type` values that this authorization
server supports. The array values used are the same as those used with the `response_types`
parameter defined by "OAuth 2.0 Dynamic Client Registration Protocol" [[RFC7591](https://www.rfc-editor.org/rfc/rfc7591)].
"""

response_modes_supported: Optional[List[str]] = None
"""
JSON array containing a list of the OAuth 2.0 `response_mode` values that this
authorization server supports, as specified in "OAuth 2.0 Multiple Response Type Encoding Practices"
[[OAuth.Responses](https://datatracker.ietf.org/doc/html/rfc8414#ref-OAuth.Responses)].

If omitted, the default is ["query", "fragment"]. The response mode value `"form_post"` is
also defined in "OAuth 2.0 Form Post Response Mode" [[OAuth.FormPost](https://datatracker.ietf.org/doc/html/rfc8414#ref-OAuth.Post)].
"""

grant_types_supported: Optional[List[str]] = None
"""
JSON array containing a list of the OAuth 2.0 grant type values that this authorization server supports.
The array values used are the same as those used with the `grant_types` parameter defined by
"OAuth 2.0 Dynamic Client Registration Protocol" [[RFC7591](https://www.rfc-editor.org/rfc/rfc7591)].

If omitted, the default value is ["authorization_code", "implicit"].
"""

token_endpoint_auth_methods_supported: Optional[List[str]] = None
token_endpoint_auth_signing_alg_values_supported: Optional[List[str]] = None
service_documentation: Optional[str] = None
ui_locales_supported: Optional[List[str]] = None
op_policy_uri: Optional[str] = None
op_tos_uri: Optional[str] = None

revocation_endpoint: Optional[str] = None
"""
URL of the authorization server's OAuth 2.0 revocation endpoint [[RFC7009](https://www.rfc-editor.org/rfc/rfc7009)].
"""

revocation_endpoint_auth_methods_supported: Optional[List[str]] = None
revocation_endpoint_auth_signing_alg_values_supported: Optional[List[str]] = None

introspection_endpoint: Optional[str] = None
"""
URL of the authorization server's OAuth 2.0 introspection endpoint [[RFC7662](https://www.rfc-editor.org/rfc/rfc7662)].
"""

introspection_endpoint_auth_methods_supported: Optional[List[str]] = None
introspection_endpoint_auth_signing_alg_values_supported: Optional[List[str]] = None

code_challenge_methods_supported: Optional[List[str]] = None
"""
JSON array containing a list of Proof Key for Code Exchange (PKCE) [[RFC7636](https://www.rfc-editor.org/rfc/rfc7636)]
code challenge methods supported by this authorization server.
"""


class AuthServerType(str, Enum):
"""
The type of the authorization server. This information should be provided by the server
configuration and indicates whether the server is an OAuth 2.0 or OpenID Connect (OIDC)
authorization server.
"""

OAUTH = "oauth"
OIDC = "oidc"


class AuthServerConfig(BaseModel):
"""
Configuration for the remote authorization server integrated with the MCP server.
"""

metadata: AuthorizationServerMetadata
"""
The metadata of the authorization server, which should conform to the MCP specification
(based on OAuth 2.0 Authorization Server Metadata).

This metadata is typically fetched from the server's well-known endpoint (OAuth 2.0
Authorization Server Metadata or OpenID Connect Discovery); it can also be provided
directly in the configuration if the server does not support such endpoints.

See:
- OAuth 2.0 Authorization Server Metadata: https://datatracker.ietf.org/doc/html/rfc8414
- OpenID Connect Discovery: https://openid.net/specs/openid-connect-discovery-1_0.html
"""

type: AuthServerType
"""
The type of the authorization server. See `AuthServerType` for possible values.
"""


class ServerMetadataPaths(str, Enum):
"""
Enum for server metadata paths.
This is used to define the standard paths for OAuth and OIDC well-known URLs.
"""

OAUTH = "/.well-known/oauth-authorization-server"
OIDC = "/.well-known/openid-configuration"
20 changes: 7 additions & 13 deletions mcpauth/middleware/create_bearer_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@
from ..types import VerifyAccessTokenFunction, Record


class BaseBearerAuthConfig(BaseModel):
class BearerAuthConfig(BaseModel):
"""
Base configuration for the Bearer auth handler.
Configuration for the Bearer auth handler.
"""

issuer: str
"""
The expected issuer of the access token. This should be a valid URL.
"""

audience: Optional[str] = None
Expand All @@ -42,17 +47,6 @@ class BaseBearerAuthConfig(BaseModel):
"""


class BearerAuthConfig(BaseBearerAuthConfig):
"""
Configuration for the Bearer auth handler.
"""

issuer: str
"""
The expected issuer of the access token. This should be a valid URL.
"""


def get_bearer_token_from_headers(headers: Headers) -> str:
"""
Extract the Bearer token from the request headers.
Expand Down
7 changes: 0 additions & 7 deletions mcpauth/models/__init__.py

This file was deleted.

39 changes: 0 additions & 39 deletions mcpauth/models/auth_server.py

This file was deleted.

Loading