Rename OIDCProxy -> OIDCDCRProxy

This commit is contained in:
Jeremiah Lowin 2025-10-20 16:01:36 -04:00
commit deb0c3ea95
9 changed files with 388 additions and 1661 deletions

View file

@ -41,7 +41,7 @@ Get the OIDC configuration for the specified config URL.
- `timeout_seconds`: HTTP request timeout in seconds
### `OIDCProxy` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/oidc_proxy.py#L172" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `OIDCDCRProxy` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/oidc_proxy.py#L172" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
OAuth provider that wraps OAuthProxy to provide configuration via an OIDC configuration URL.

View file

@ -39,10 +39,10 @@ Here's how to implement the OIDC proxy with any provider:
```python
from fastmcp import FastMCP
from fastmcp.server.auth.oidc_proxy import OIDCProxy
from fastmcp.server.auth.oidc_dcr_proxy import OIDCDCRProxy
# Create the OIDC proxy
auth = OIDCProxy(
auth = OIDCDCRProxy(
# Provider's configuration URL
config_url="https://provider.com/.well-known/openid-configuration",
@ -62,7 +62,7 @@ mcp = FastMCP(name="My Server", auth=auth)
### Configuration Parameters
<Card icon="code" title="OIDCProxy Parameters">
<Card icon="code" title="OIDCDCRProxy Parameters">
<ParamField body="config_url" type="str" required>
URL of your OAuth provider's OIDC configuration
</ParamField>
@ -136,7 +136,7 @@ Set this if your provider requires a specific authentication method and the defa
from fastmcp.utilities.storage import InMemoryStorage
# Use in-memory storage for testing (clients lost on restart)
auth = OIDCProxy(..., client_storage=InMemoryStorage())
auth = OIDCDCRProxy(..., client_storage=InMemoryStorage())
```
</ParamField>

View file

@ -0,0 +1,350 @@
"""OIDC Proxy Provider for FastMCP.
This provider acts as a transparent proxy to an upstream OIDC compliant Authorization
Server. It leverages the OAuthProxy class to handle Dynamic Client Registration and
forwarding of all OAuth flows.
This implementation is based on:
OpenID Connect Discovery 1.0 - https://openid.net/specs/openid-connect-discovery-1_0.html
OAuth 2.0 Authorization Server Metadata - https://datatracker.ietf.org/doc/html/rfc8414
"""
from collections.abc import Sequence
import httpx
from key_value.aio.protocols import AsyncKeyValue
from pydantic import AnyHttpUrl, BaseModel, model_validator
from typing_extensions import Self
from fastmcp.server.auth import TokenVerifier
from fastmcp.server.auth.oauth_dcr_proxy import OAuthDCRProxy
from fastmcp.server.auth.providers.jwt import JWTVerifier
from fastmcp.utilities.logging import get_logger
logger = get_logger(__name__)
class OIDCConfiguration(BaseModel):
"""OIDC Configuration.
See:
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
https://datatracker.ietf.org/doc/html/rfc8414#section-2
"""
strict: bool = True
# OpenID Connect Discovery 1.0
issuer: AnyHttpUrl | str | None = None # Strict
authorization_endpoint: AnyHttpUrl | str | None = None # Strict
token_endpoint: AnyHttpUrl | str | None = None # Strict
userinfo_endpoint: AnyHttpUrl | str | None = None
jwks_uri: AnyHttpUrl | str | None = None # Strict
registration_endpoint: AnyHttpUrl | str | None = None
scopes_supported: Sequence[str] | None = None
response_types_supported: Sequence[str] | None = None # Strict
response_modes_supported: Sequence[str] | None = None
grant_types_supported: Sequence[str] | None = None
acr_values_supported: Sequence[str] | None = None
subject_types_supported: Sequence[str] | None = None # Strict
id_token_signing_alg_values_supported: Sequence[str] | None = None # Strict
id_token_encryption_alg_values_supported: Sequence[str] | None = None
id_token_encryption_enc_values_supported: Sequence[str] | None = None
userinfo_signing_alg_values_supported: Sequence[str] | None = None
userinfo_encryption_alg_values_supported: Sequence[str] | None = None
userinfo_encryption_enc_values_supported: Sequence[str] | None = None
request_object_signing_alg_values_supported: Sequence[str] | None = None
request_object_encryption_alg_values_supported: Sequence[str] | None = None
request_object_encryption_enc_values_supported: Sequence[str] | None = None
token_endpoint_auth_methods_supported: Sequence[str] | None = None
token_endpoint_auth_signing_alg_values_supported: Sequence[str] | None = None
display_values_supported: Sequence[str] | None = None
claim_types_supported: Sequence[str] | None = None
claims_supported: Sequence[str] | None = None
service_documentation: AnyHttpUrl | str | None = None
claims_locales_supported: Sequence[str] | None = None
ui_locales_supported: Sequence[str] | None = None
claims_parameter_supported: bool | None = None
request_parameter_supported: bool | None = None
request_uri_parameter_supported: bool | None = None
require_request_uri_registration: bool | None = None
op_policy_uri: AnyHttpUrl | str | None = None
op_tos_uri: AnyHttpUrl | str | None = None
# OAuth 2.0 Authorization Server Metadata
revocation_endpoint: AnyHttpUrl | str | None = None
revocation_endpoint_auth_methods_supported: Sequence[str] | None = None
revocation_endpoint_auth_signing_alg_values_supported: Sequence[str] | None = None
introspection_endpoint: AnyHttpUrl | str | None = None
introspection_endpoint_auth_methods_supported: Sequence[str] | None = None
introspection_endpoint_auth_signing_alg_values_supported: Sequence[str] | None = (
None
)
code_challenge_methods_supported: Sequence[str] | None = None
signed_metadata: str | None = None
@model_validator(mode="after")
def _enforce_strict(self) -> Self:
"""Enforce strict rules."""
if not self.strict:
return self
def enforce(attr: str, is_url: bool = False) -> None:
value = getattr(self, attr, None)
if not value:
message = f"Missing required configuration metadata: {attr}"
logger.error(message)
raise ValueError(message)
if not is_url or isinstance(value, AnyHttpUrl):
return
try:
AnyHttpUrl(value)
except Exception:
message = f"Invalid URL for configuration metadata: {attr}"
logger.error(message)
raise ValueError(message)
enforce("issuer", True)
enforce("authorization_endpoint", True)
enforce("token_endpoint", True)
enforce("jwks_uri", True)
enforce("response_types_supported")
enforce("subject_types_supported")
enforce("id_token_signing_alg_values_supported")
return self
@classmethod
def get_oidc_configuration(
cls, config_url: AnyHttpUrl, *, strict: bool | None, timeout_seconds: int | None
) -> Self:
"""Get the OIDC configuration for the specified config URL.
Args:
config_url: The OIDC config URL
strict: The strict flag for the configuration
timeout_seconds: HTTP request timeout in seconds
"""
get_kwargs = {}
if timeout_seconds is not None:
get_kwargs["timeout"] = timeout_seconds
try:
response = httpx.get(str(config_url), **get_kwargs)
response.raise_for_status()
config_data = response.json()
if strict is not None:
config_data["strict"] = strict
return cls.model_validate(config_data)
except Exception:
logger.exception(
f"Unable to get OIDC configuration for config url: {config_url}"
)
raise
class OIDCDCRProxy(OAuthDCRProxy):
"""OAuth provider that wraps OAuthDCRProxy to provide configuration via an OIDC configuration URL.
This provider makes it easier to add OAuth protection for any upstream provider
that is OIDC compliant.
Example:
```python
from fastmcp import FastMCP
from fastmcp.server.auth.oidc_dcr_proxy import OIDCDCRProxy
# Simple OIDC based protection
auth = OIDCDCRProxy(
config_url="https://oidc.config.url",
client_id="your-oidc-client-id",
client_secret="your-oidc-client-secret",
base_url="https://your.server.url",
)
mcp = FastMCP("My Protected Server", auth=auth)
```
"""
oidc_config: OIDCConfiguration
def __init__(
self,
*,
# OIDC configuration
config_url: AnyHttpUrl | str,
strict: bool | None = None,
# Upstream server configuration
client_id: str,
client_secret: str,
audience: str | None = None,
timeout_seconds: int | None = None,
# Token verifier
algorithm: str | None = None,
required_scopes: list[str] | None = None,
# FastMCP server configuration
base_url: AnyHttpUrl | str,
issuer_url: AnyHttpUrl | str | None = None,
redirect_path: str | None = None,
# Client configuration
allowed_client_redirect_uris: list[str] | None = None,
client_storage: AsyncKeyValue | None = None,
# Token validation configuration
token_endpoint_auth_method: str | None = None,
) -> None:
"""Initialize the OIDC proxy provider.
Args:
config_url: URL of upstream configuration
strict: Optional strict flag for the configuration
client_id: Client ID registered with upstream server
client_secret: Client secret for upstream server
audience: Audience for upstream server
timeout_seconds: HTTP request timeout in seconds
algorithm: Token verifier algorithm
required_scopes: Required OAuth scopes
base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
to avoid 404s during discovery when mounting under a path.
redirect_path: Redirect path configured in upstream OAuth app (defaults to "/auth/callback")
allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
Patterns support wildcards (e.g., "http://localhost:*", "https://*.example.com/*").
If None (default), only localhost redirect URIs are allowed.
If empty list, all redirect URIs are allowed (not recommended for production).
These are for MCP clients performing loopback redirects, NOT for the upstream OAuth app.
client_storage: An AsyncKeyValue-compatible store for client registrations, registrations are stored in memory if not provided
token_endpoint_auth_method: Token endpoint authentication method for upstream server.
Common values: "client_secret_basic", "client_secret_post", "none".
If None, authlib will use its default (typically "client_secret_basic").
"""
if not config_url:
raise ValueError("Missing required config URL")
if not client_id:
raise ValueError("Missing required client id")
if not client_secret:
raise ValueError("Missing required client secret")
if not base_url:
raise ValueError("Missing required base URL")
if isinstance(config_url, str):
config_url = AnyHttpUrl(config_url)
self.oidc_config = self.get_oidc_configuration(
config_url, strict, timeout_seconds
)
if (
not self.oidc_config.authorization_endpoint
or not self.oidc_config.token_endpoint
):
logger.debug(f"Invalid OIDC Configuration: {self.oidc_config}")
raise ValueError("Missing required OIDC endpoints")
revocation_endpoint = (
str(self.oidc_config.revocation_endpoint)
if self.oidc_config.revocation_endpoint
else None
)
token_verifier = self.get_token_verifier(
algorithm=algorithm,
audience=audience,
required_scopes=required_scopes,
timeout_seconds=timeout_seconds,
)
init_kwargs = {
"upstream_authorization_endpoint": str(
self.oidc_config.authorization_endpoint
),
"upstream_token_endpoint": str(self.oidc_config.token_endpoint),
"upstream_client_id": client_id,
"upstream_client_secret": client_secret,
"upstream_revocation_endpoint": revocation_endpoint,
"token_verifier": token_verifier,
"base_url": base_url,
"issuer_url": issuer_url or base_url,
"service_documentation_url": self.oidc_config.service_documentation,
"allowed_client_redirect_uris": allowed_client_redirect_uris,
"client_storage": client_storage,
"token_endpoint_auth_method": token_endpoint_auth_method,
}
if redirect_path:
init_kwargs["redirect_path"] = redirect_path
if audience:
extra_params = {"audience": audience}
init_kwargs["extra_authorize_params"] = extra_params
init_kwargs["extra_token_params"] = extra_params
super().__init__(**init_kwargs)
def get_oidc_configuration(
self,
config_url: AnyHttpUrl,
strict: bool | None,
timeout_seconds: int | None,
) -> OIDCConfiguration:
"""Gets the OIDC configuration for the specified configuration URL.
Args:
config_url: The OIDC configuration URL
strict: The strict flag for the configuration
timeout_seconds: HTTP request timeout in seconds
"""
return OIDCConfiguration.get_oidc_configuration(
config_url, strict=strict, timeout_seconds=timeout_seconds
)
def get_token_verifier(
self,
*,
algorithm: str | None = None,
audience: str | None = None,
required_scopes: list[str] | None = None,
timeout_seconds: int | None = None,
) -> TokenVerifier:
"""Creates the token verifier for the specified OIDC configuration and arguments.
Args:
algorithm: Optional token verifier algorithm
audience: Optional token verifier audience
required_scopes: Optional token verifier required_scopes
timeout_seconds: HTTP request timeout in seconds
"""
return JWTVerifier(
jwks_uri=str(self.oidc_config.jwks_uri),
issuer=str(self.oidc_config.issuer),
algorithm=algorithm,
audience=audience,
required_scopes=required_scopes,
)

View file

@ -1,350 +1,24 @@
"""OIDC Proxy Provider for FastMCP.
"""Backwards compatibility shim for oidc_proxy.py
This provider acts as a transparent proxy to an upstream OIDC compliant Authorization
Server. It leverages the OAuthProxy class to handle Dynamic Client Registration and
forwarding of all OAuth flows.
This implementation is based on:
OpenID Connect Discovery 1.0 - https://openid.net/specs/openid-connect-discovery-1_0.html
OAuth 2.0 Authorization Server Metadata - https://datatracker.ietf.org/doc/html/rfc8414
The OIDCProxy class has been moved to fastmcp.server.auth.oidc_dcr_proxy.OIDCDCRProxy
for better organization. This module provides a backwards-compatible import.
"""
from collections.abc import Sequence
import warnings
import httpx
from key_value.aio.protocols import AsyncKeyValue
from pydantic import AnyHttpUrl, BaseModel, model_validator
from typing_extensions import Self
import fastmcp
from fastmcp.server.auth.oidc_dcr_proxy import OIDCDCRProxy as OIDCProxy
from fastmcp.server.auth import TokenVerifier
from fastmcp.server.auth.oauth_dcr_proxy import OAuthDCRProxy
from fastmcp.server.auth.providers.jwt import JWTVerifier
from fastmcp.utilities.logging import get_logger
# Re-export for backwards compatibility
__all__ = ["OIDCProxy"]
logger = get_logger(__name__)
class OIDCConfiguration(BaseModel):
"""OIDC Configuration.
See:
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
https://datatracker.ietf.org/doc/html/rfc8414#section-2
"""
strict: bool = True
# OpenID Connect Discovery 1.0
issuer: AnyHttpUrl | str | None = None # Strict
authorization_endpoint: AnyHttpUrl | str | None = None # Strict
token_endpoint: AnyHttpUrl | str | None = None # Strict
userinfo_endpoint: AnyHttpUrl | str | None = None
jwks_uri: AnyHttpUrl | str | None = None # Strict
registration_endpoint: AnyHttpUrl | str | None = None
scopes_supported: Sequence[str] | None = None
response_types_supported: Sequence[str] | None = None # Strict
response_modes_supported: Sequence[str] | None = None
grant_types_supported: Sequence[str] | None = None
acr_values_supported: Sequence[str] | None = None
subject_types_supported: Sequence[str] | None = None # Strict
id_token_signing_alg_values_supported: Sequence[str] | None = None # Strict
id_token_encryption_alg_values_supported: Sequence[str] | None = None
id_token_encryption_enc_values_supported: Sequence[str] | None = None
userinfo_signing_alg_values_supported: Sequence[str] | None = None
userinfo_encryption_alg_values_supported: Sequence[str] | None = None
userinfo_encryption_enc_values_supported: Sequence[str] | None = None
request_object_signing_alg_values_supported: Sequence[str] | None = None
request_object_encryption_alg_values_supported: Sequence[str] | None = None
request_object_encryption_enc_values_supported: Sequence[str] | None = None
token_endpoint_auth_methods_supported: Sequence[str] | None = None
token_endpoint_auth_signing_alg_values_supported: Sequence[str] | None = None
display_values_supported: Sequence[str] | None = None
claim_types_supported: Sequence[str] | None = None
claims_supported: Sequence[str] | None = None
service_documentation: AnyHttpUrl | str | None = None
claims_locales_supported: Sequence[str] | None = None
ui_locales_supported: Sequence[str] | None = None
claims_parameter_supported: bool | None = None
request_parameter_supported: bool | None = None
request_uri_parameter_supported: bool | None = None
require_request_uri_registration: bool | None = None
op_policy_uri: AnyHttpUrl | str | None = None
op_tos_uri: AnyHttpUrl | str | None = None
# OAuth 2.0 Authorization Server Metadata
revocation_endpoint: AnyHttpUrl | str | None = None
revocation_endpoint_auth_methods_supported: Sequence[str] | None = None
revocation_endpoint_auth_signing_alg_values_supported: Sequence[str] | None = None
introspection_endpoint: AnyHttpUrl | str | None = None
introspection_endpoint_auth_methods_supported: Sequence[str] | None = None
introspection_endpoint_auth_signing_alg_values_supported: Sequence[str] | None = (
None
# Deprecated in 2.13
if fastmcp.settings.deprecation_warnings:
warnings.warn(
"The `fastmcp.server.auth.oidc_proxy` module is deprecated "
"and will be removed in a future version. "
"Please use `fastmcp.server.auth.oidc_dcr_proxy.OIDCDCRProxy` "
"instead of this module's OIDCProxy.",
DeprecationWarning,
stacklevel=2,
)
code_challenge_methods_supported: Sequence[str] | None = None
signed_metadata: str | None = None
@model_validator(mode="after")
def _enforce_strict(self) -> Self:
"""Enforce strict rules."""
if not self.strict:
return self
def enforce(attr: str, is_url: bool = False) -> None:
value = getattr(self, attr, None)
if not value:
message = f"Missing required configuration metadata: {attr}"
logger.error(message)
raise ValueError(message)
if not is_url or isinstance(value, AnyHttpUrl):
return
try:
AnyHttpUrl(value)
except Exception:
message = f"Invalid URL for configuration metadata: {attr}"
logger.error(message)
raise ValueError(message)
enforce("issuer", True)
enforce("authorization_endpoint", True)
enforce("token_endpoint", True)
enforce("jwks_uri", True)
enforce("response_types_supported")
enforce("subject_types_supported")
enforce("id_token_signing_alg_values_supported")
return self
@classmethod
def get_oidc_configuration(
cls, config_url: AnyHttpUrl, *, strict: bool | None, timeout_seconds: int | None
) -> Self:
"""Get the OIDC configuration for the specified config URL.
Args:
config_url: The OIDC config URL
strict: The strict flag for the configuration
timeout_seconds: HTTP request timeout in seconds
"""
get_kwargs = {}
if timeout_seconds is not None:
get_kwargs["timeout"] = timeout_seconds
try:
response = httpx.get(str(config_url), **get_kwargs)
response.raise_for_status()
config_data = response.json()
if strict is not None:
config_data["strict"] = strict
return cls.model_validate(config_data)
except Exception:
logger.exception(
f"Unable to get OIDC configuration for config url: {config_url}"
)
raise
class OIDCProxy(OAuthDCRProxy):
"""OAuth provider that wraps OAuthProxy to provide configuration via an OIDC configuration URL.
This provider makes it easier to add OAuth protection for any upstream provider
that is OIDC compliant.
Example:
```python
from fastmcp import FastMCP
from fastmcp.server.auth.oidc_proxy import OIDCProxy
# Simple OIDC based protection
auth = OIDCProxy(
config_url="https://oidc.config.url",
client_id="your-oidc-client-id",
client_secret="your-oidc-client-secret",
base_url="https://your.server.url",
)
mcp = FastMCP("My Protected Server", auth=auth)
```
"""
oidc_config: OIDCConfiguration
def __init__(
self,
*,
# OIDC configuration
config_url: AnyHttpUrl | str,
strict: bool | None = None,
# Upstream server configuration
client_id: str,
client_secret: str,
audience: str | None = None,
timeout_seconds: int | None = None,
# Token verifier
algorithm: str | None = None,
required_scopes: list[str] | None = None,
# FastMCP server configuration
base_url: AnyHttpUrl | str,
issuer_url: AnyHttpUrl | str | None = None,
redirect_path: str | None = None,
# Client configuration
allowed_client_redirect_uris: list[str] | None = None,
client_storage: AsyncKeyValue | None = None,
# Token validation configuration
token_endpoint_auth_method: str | None = None,
) -> None:
"""Initialize the OIDC proxy provider.
Args:
config_url: URL of upstream configuration
strict: Optional strict flag for the configuration
client_id: Client ID registered with upstream server
client_secret: Client secret for upstream server
audience: Audience for upstream server
timeout_seconds: HTTP request timeout in seconds
algorithm: Token verifier algorithm
required_scopes: Required OAuth scopes
base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
to avoid 404s during discovery when mounting under a path.
redirect_path: Redirect path configured in upstream OAuth app (defaults to "/auth/callback")
allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
Patterns support wildcards (e.g., "http://localhost:*", "https://*.example.com/*").
If None (default), only localhost redirect URIs are allowed.
If empty list, all redirect URIs are allowed (not recommended for production).
These are for MCP clients performing loopback redirects, NOT for the upstream OAuth app.
client_storage: An AsyncKeyValue-compatible store for client registrations, registrations are stored in memory if not provided
token_endpoint_auth_method: Token endpoint authentication method for upstream server.
Common values: "client_secret_basic", "client_secret_post", "none".
If None, authlib will use its default (typically "client_secret_basic").
"""
if not config_url:
raise ValueError("Missing required config URL")
if not client_id:
raise ValueError("Missing required client id")
if not client_secret:
raise ValueError("Missing required client secret")
if not base_url:
raise ValueError("Missing required base URL")
if isinstance(config_url, str):
config_url = AnyHttpUrl(config_url)
self.oidc_config = self.get_oidc_configuration(
config_url, strict, timeout_seconds
)
if (
not self.oidc_config.authorization_endpoint
or not self.oidc_config.token_endpoint
):
logger.debug(f"Invalid OIDC Configuration: {self.oidc_config}")
raise ValueError("Missing required OIDC endpoints")
revocation_endpoint = (
str(self.oidc_config.revocation_endpoint)
if self.oidc_config.revocation_endpoint
else None
)
token_verifier = self.get_token_verifier(
algorithm=algorithm,
audience=audience,
required_scopes=required_scopes,
timeout_seconds=timeout_seconds,
)
init_kwargs = {
"upstream_authorization_endpoint": str(
self.oidc_config.authorization_endpoint
),
"upstream_token_endpoint": str(self.oidc_config.token_endpoint),
"upstream_client_id": client_id,
"upstream_client_secret": client_secret,
"upstream_revocation_endpoint": revocation_endpoint,
"token_verifier": token_verifier,
"base_url": base_url,
"issuer_url": issuer_url or base_url,
"service_documentation_url": self.oidc_config.service_documentation,
"allowed_client_redirect_uris": allowed_client_redirect_uris,
"client_storage": client_storage,
"token_endpoint_auth_method": token_endpoint_auth_method,
}
if redirect_path:
init_kwargs["redirect_path"] = redirect_path
if audience:
extra_params = {"audience": audience}
init_kwargs["extra_authorize_params"] = extra_params
init_kwargs["extra_token_params"] = extra_params
super().__init__(**init_kwargs)
def get_oidc_configuration(
self,
config_url: AnyHttpUrl,
strict: bool | None,
timeout_seconds: int | None,
) -> OIDCConfiguration:
"""Gets the OIDC configuration for the specified configuration URL.
Args:
config_url: The OIDC configuration URL
strict: The strict flag for the configuration
timeout_seconds: HTTP request timeout in seconds
"""
return OIDCConfiguration.get_oidc_configuration(
config_url, strict=strict, timeout_seconds=timeout_seconds
)
def get_token_verifier(
self,
*,
algorithm: str | None = None,
audience: str | None = None,
required_scopes: list[str] | None = None,
timeout_seconds: int | None = None,
) -> TokenVerifier:
"""Creates the token verifier for the specified OIDC configuration and arguments.
Args:
algorithm: Optional token verifier algorithm
audience: Optional token verifier audience
required_scopes: Optional token verifier required_scopes
timeout_seconds: HTTP request timeout in seconds
"""
return JWTVerifier(
jwks_uri=str(self.oidc_config.jwks_uri),
issuer=str(self.oidc_config.issuer),
algorithm=algorithm,
audience=audience,
required_scopes=required_scopes,
)

View file

@ -25,7 +25,7 @@ from key_value.aio.protocols import AsyncKeyValue
from pydantic import AnyHttpUrl, SecretStr, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
from fastmcp.server.auth.oidc_proxy import OIDCProxy
from fastmcp.server.auth.oidc_dcr_proxy import OIDCDCRProxy
from fastmcp.settings import ENV_FILE
from fastmcp.utilities.auth import parse_scopes
from fastmcp.utilities.logging import get_logger
@ -59,7 +59,7 @@ class Auth0ProviderSettings(BaseSettings):
return parse_scopes(v)
class Auth0Provider(OIDCProxy):
class Auth0Provider(OIDCDCRProxy):
"""An Auth0 provider implementation for FastMCP.
This provider is a complete Auth0 integration that's ready to use with

View file

@ -29,7 +29,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
from fastmcp.server.auth import TokenVerifier
from fastmcp.server.auth.auth import AccessToken
from fastmcp.server.auth.oidc_proxy import OIDCProxy
from fastmcp.server.auth.oidc_dcr_proxy import OIDCDCRProxy
from fastmcp.server.auth.providers.jwt import JWTVerifier
from fastmcp.settings import ENV_FILE
from fastmcp.utilities.auth import parse_scopes
@ -91,7 +91,7 @@ class AWSCognitoTokenVerifier(JWTVerifier):
)
class AWSCognitoProvider(OIDCProxy):
class AWSCognitoProvider(OIDCDCRProxy):
"""Complete AWS Cognito OAuth provider for FastMCP.
This provider makes it trivial to add AWS Cognito OAuth protection to any

View file

@ -7,7 +7,7 @@ import pytest
from httpx import Response
from pydantic import AnyHttpUrl
from fastmcp.server.auth.oidc_proxy import OIDCConfiguration, OIDCProxy
from fastmcp.server.auth.oidc_dcr_proxy import OIDCConfiguration, OIDCDCRProxy
from fastmcp.server.auth.providers.jwt import JWTVerifier
TEST_ISSUER = "https://example.com"
@ -440,7 +440,7 @@ def validate_proxy(mock_get, proxy, oidc_config):
assert proxy.oidc_config == oidc_config
class TestOIDCProxyInitialization:
class TestOIDCDCRProxyInitialization:
"""Tests for OIDC proxy initialization."""
def test_default_initialization(self, valid_oidc_configuration_dict):
@ -453,7 +453,7 @@ class TestOIDCProxyInitialization:
)
mock_get.return_value = oidc_config
proxy = OIDCProxy(
proxy = OIDCDCRProxy(
config_url=TEST_CONFIG_URL,
client_id=TEST_CLIENT_ID,
client_secret=TEST_CLIENT_SECRET,
@ -472,7 +472,7 @@ class TestOIDCProxyInitialization:
)
mock_get.return_value = oidc_config
proxy = OIDCProxy(
proxy = OIDCDCRProxy(
config_url=TEST_CONFIG_URL,
client_id=TEST_CLIENT_ID,
client_secret=TEST_CLIENT_SECRET,
@ -495,7 +495,7 @@ class TestOIDCProxyInitialization:
)
mock_get.return_value = oidc_config
proxy = OIDCProxy(
proxy = OIDCDCRProxy(
config_url=TEST_CONFIG_URL,
client_id=TEST_CLIENT_ID,
client_secret=TEST_CLIENT_SECRET,
@ -523,7 +523,7 @@ class TestOIDCProxyInitialization:
)
mock_get.return_value = oidc_config
proxy = OIDCProxy(
proxy = OIDCDCRProxy(
config_url=TEST_CONFIG_URL,
client_id=TEST_CLIENT_ID,
client_secret=TEST_CLIENT_SECRET,
@ -548,7 +548,7 @@ class TestOIDCProxyInitialization:
)
mock_get.return_value = oidc_config
proxy = OIDCProxy(
proxy = OIDCDCRProxy(
config_url=TEST_CONFIG_URL,
client_id=TEST_CLIENT_ID,
client_secret=TEST_CLIENT_SECRET,
@ -577,7 +577,7 @@ class TestOIDCProxyInitialization:
mock_get.return_value = oidc_config
with pytest.raises(ValueError, match="Missing required config URL"):
OIDCProxy(
OIDCDCRProxy(
config_url=None, # type: ignore
client_id=TEST_CLIENT_ID,
client_secret=TEST_CLIENT_SECRET,
@ -597,7 +597,7 @@ class TestOIDCProxyInitialization:
mock_get.return_value = oidc_config
with pytest.raises(ValueError, match="Missing required client id"):
OIDCProxy(
OIDCDCRProxy(
config_url=TEST_CONFIG_URL,
client_id=None, # type: ignore
client_secret=TEST_CLIENT_SECRET,
@ -617,7 +617,7 @@ class TestOIDCProxyInitialization:
mock_get.return_value = oidc_config
with pytest.raises(ValueError, match="Missing required client secret"):
OIDCProxy(
OIDCDCRProxy(
config_url=TEST_CONFIG_URL,
client_id=TEST_CLIENT_ID,
client_secret=None, # type: ignore
@ -637,7 +637,7 @@ class TestOIDCProxyInitialization:
mock_get.return_value = oidc_config
with pytest.raises(ValueError, match="Missing required base URL"):
OIDCProxy(
OIDCDCRProxy(
config_url=TEST_CONFIG_URL,
client_id=TEST_CLIENT_ID,
client_secret=TEST_CLIENT_SECRET,

View file

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest
from fastmcp.server.auth.oidc_proxy import OIDCConfiguration
from fastmcp.server.auth.oidc_dcr_proxy import OIDCConfiguration
from fastmcp.server.auth.providers.auth0 import Auth0Provider, Auth0ProviderSettings
from fastmcp.server.auth.providers.jwt import JWTVerifier

File diff suppressed because it is too large Load diff