fastmcp 2.12.5__py3-none-any.whl → 2.14.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastmcp/__init__.py +2 -23
- fastmcp/cli/__init__.py +0 -3
- fastmcp/cli/__main__.py +5 -0
- fastmcp/cli/cli.py +19 -33
- fastmcp/cli/install/claude_code.py +6 -6
- fastmcp/cli/install/claude_desktop.py +3 -3
- fastmcp/cli/install/cursor.py +18 -12
- fastmcp/cli/install/gemini_cli.py +3 -3
- fastmcp/cli/install/mcp_json.py +3 -3
- fastmcp/cli/install/shared.py +0 -15
- fastmcp/cli/run.py +13 -8
- fastmcp/cli/tasks.py +110 -0
- fastmcp/client/__init__.py +9 -9
- fastmcp/client/auth/oauth.py +123 -225
- fastmcp/client/client.py +697 -95
- fastmcp/client/elicitation.py +11 -5
- fastmcp/client/logging.py +18 -14
- fastmcp/client/messages.py +7 -5
- fastmcp/client/oauth_callback.py +85 -171
- fastmcp/client/roots.py +2 -1
- fastmcp/client/sampling.py +1 -1
- fastmcp/client/tasks.py +614 -0
- fastmcp/client/transports.py +117 -30
- fastmcp/contrib/component_manager/__init__.py +1 -1
- fastmcp/contrib/component_manager/component_manager.py +2 -2
- fastmcp/contrib/component_manager/component_service.py +10 -26
- fastmcp/contrib/mcp_mixin/README.md +32 -1
- fastmcp/contrib/mcp_mixin/__init__.py +2 -2
- fastmcp/contrib/mcp_mixin/mcp_mixin.py +14 -2
- fastmcp/dependencies.py +25 -0
- fastmcp/experimental/sampling/handlers/openai.py +3 -3
- fastmcp/experimental/server/openapi/__init__.py +20 -21
- fastmcp/experimental/utilities/openapi/__init__.py +16 -47
- fastmcp/mcp_config.py +3 -4
- fastmcp/prompts/__init__.py +1 -1
- fastmcp/prompts/prompt.py +54 -51
- fastmcp/prompts/prompt_manager.py +16 -101
- fastmcp/resources/__init__.py +5 -5
- fastmcp/resources/resource.py +43 -21
- fastmcp/resources/resource_manager.py +9 -168
- fastmcp/resources/template.py +161 -61
- fastmcp/resources/types.py +30 -24
- fastmcp/server/__init__.py +1 -1
- fastmcp/server/auth/__init__.py +9 -14
- fastmcp/server/auth/auth.py +197 -46
- fastmcp/server/auth/handlers/authorize.py +326 -0
- fastmcp/server/auth/jwt_issuer.py +236 -0
- fastmcp/server/auth/middleware.py +96 -0
- fastmcp/server/auth/oauth_proxy.py +1469 -298
- fastmcp/server/auth/oidc_proxy.py +91 -20
- fastmcp/server/auth/providers/auth0.py +40 -21
- fastmcp/server/auth/providers/aws.py +29 -3
- fastmcp/server/auth/providers/azure.py +312 -131
- fastmcp/server/auth/providers/debug.py +114 -0
- fastmcp/server/auth/providers/descope.py +86 -29
- fastmcp/server/auth/providers/discord.py +308 -0
- fastmcp/server/auth/providers/github.py +29 -8
- fastmcp/server/auth/providers/google.py +48 -9
- fastmcp/server/auth/providers/in_memory.py +29 -5
- fastmcp/server/auth/providers/introspection.py +281 -0
- fastmcp/server/auth/providers/jwt.py +48 -31
- fastmcp/server/auth/providers/oci.py +233 -0
- fastmcp/server/auth/providers/scalekit.py +238 -0
- fastmcp/server/auth/providers/supabase.py +188 -0
- fastmcp/server/auth/providers/workos.py +35 -17
- fastmcp/server/context.py +236 -116
- fastmcp/server/dependencies.py +503 -18
- fastmcp/server/elicitation.py +286 -48
- fastmcp/server/event_store.py +177 -0
- fastmcp/server/http.py +71 -20
- fastmcp/server/low_level.py +165 -2
- fastmcp/server/middleware/__init__.py +1 -1
- fastmcp/server/middleware/caching.py +476 -0
- fastmcp/server/middleware/error_handling.py +14 -10
- fastmcp/server/middleware/logging.py +50 -39
- fastmcp/server/middleware/middleware.py +29 -16
- fastmcp/server/middleware/rate_limiting.py +3 -3
- fastmcp/server/middleware/tool_injection.py +116 -0
- fastmcp/server/openapi/__init__.py +35 -0
- fastmcp/{experimental/server → server}/openapi/components.py +15 -10
- fastmcp/{experimental/server → server}/openapi/routing.py +3 -3
- fastmcp/{experimental/server → server}/openapi/server.py +6 -5
- fastmcp/server/proxy.py +72 -48
- fastmcp/server/server.py +1415 -733
- fastmcp/server/tasks/__init__.py +21 -0
- fastmcp/server/tasks/capabilities.py +22 -0
- fastmcp/server/tasks/config.py +89 -0
- fastmcp/server/tasks/converters.py +205 -0
- fastmcp/server/tasks/handlers.py +356 -0
- fastmcp/server/tasks/keys.py +93 -0
- fastmcp/server/tasks/protocol.py +355 -0
- fastmcp/server/tasks/subscriptions.py +205 -0
- fastmcp/settings.py +125 -113
- fastmcp/tools/__init__.py +1 -1
- fastmcp/tools/tool.py +138 -55
- fastmcp/tools/tool_manager.py +30 -112
- fastmcp/tools/tool_transform.py +12 -21
- fastmcp/utilities/cli.py +67 -28
- fastmcp/utilities/components.py +10 -5
- fastmcp/utilities/inspect.py +79 -23
- fastmcp/utilities/json_schema.py +4 -4
- fastmcp/utilities/json_schema_type.py +8 -8
- fastmcp/utilities/logging.py +118 -8
- fastmcp/utilities/mcp_config.py +1 -2
- fastmcp/utilities/mcp_server_config/__init__.py +3 -3
- fastmcp/utilities/mcp_server_config/v1/environments/base.py +1 -2
- fastmcp/utilities/mcp_server_config/v1/environments/uv.py +6 -6
- fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +5 -5
- fastmcp/utilities/mcp_server_config/v1/schema.json +3 -0
- fastmcp/utilities/mcp_server_config/v1/sources/base.py +0 -1
- fastmcp/{experimental/utilities → utilities}/openapi/README.md +7 -35
- fastmcp/utilities/openapi/__init__.py +63 -0
- fastmcp/{experimental/utilities → utilities}/openapi/director.py +14 -15
- fastmcp/{experimental/utilities → utilities}/openapi/formatters.py +5 -5
- fastmcp/{experimental/utilities → utilities}/openapi/json_schema_converter.py +7 -3
- fastmcp/{experimental/utilities → utilities}/openapi/parser.py +37 -16
- fastmcp/utilities/tests.py +92 -5
- fastmcp/utilities/types.py +86 -16
- fastmcp/utilities/ui.py +626 -0
- {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/METADATA +24 -15
- fastmcp-2.14.0.dist-info/RECORD +156 -0
- {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/WHEEL +1 -1
- fastmcp/cli/claude.py +0 -135
- fastmcp/server/auth/providers/bearer.py +0 -25
- fastmcp/server/openapi.py +0 -1083
- fastmcp/utilities/openapi.py +0 -1568
- fastmcp/utilities/storage.py +0 -204
- fastmcp-2.12.5.dist-info/RECORD +0 -134
- fastmcp/{experimental/server → server}/openapi/README.md +0 -0
- fastmcp/{experimental/utilities → utilities}/openapi/models.py +3 -3
- fastmcp/{experimental/utilities → utilities}/openapi/schemas.py +2 -2
- {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -12,6 +12,7 @@ This implementation is based on:
|
|
|
12
12
|
from collections.abc import Sequence
|
|
13
13
|
|
|
14
14
|
import httpx
|
|
15
|
+
from key_value.aio.protocols import AsyncKeyValue
|
|
15
16
|
from pydantic import AnyHttpUrl, BaseModel, model_validator
|
|
16
17
|
from typing_extensions import Self
|
|
17
18
|
|
|
@@ -19,7 +20,6 @@ from fastmcp.server.auth import TokenVerifier
|
|
|
19
20
|
from fastmcp.server.auth.oauth_proxy import OAuthProxy
|
|
20
21
|
from fastmcp.server.auth.providers.jwt import JWTVerifier
|
|
21
22
|
from fastmcp.utilities.logging import get_logger
|
|
22
|
-
from fastmcp.utilities.storage import KVStorage
|
|
23
23
|
|
|
24
24
|
logger = get_logger(__name__)
|
|
25
25
|
|
|
@@ -123,10 +123,10 @@ class OIDCConfiguration(BaseModel):
|
|
|
123
123
|
|
|
124
124
|
try:
|
|
125
125
|
AnyHttpUrl(value)
|
|
126
|
-
except Exception:
|
|
126
|
+
except Exception as e:
|
|
127
127
|
message = f"Invalid URL for configuration metadata: {attr}"
|
|
128
128
|
logger.error(message)
|
|
129
|
-
raise ValueError(message)
|
|
129
|
+
raise ValueError(message) from e
|
|
130
130
|
|
|
131
131
|
enforce("issuer", True)
|
|
132
132
|
enforce("authorization_endpoint", True)
|
|
@@ -206,16 +206,28 @@ class OIDCProxy(OAuthProxy):
|
|
|
206
206
|
audience: str | None = None,
|
|
207
207
|
timeout_seconds: int | None = None,
|
|
208
208
|
# Token verifier
|
|
209
|
+
token_verifier: TokenVerifier | None = None,
|
|
209
210
|
algorithm: str | None = None,
|
|
210
211
|
required_scopes: list[str] | None = None,
|
|
211
212
|
# FastMCP server configuration
|
|
212
213
|
base_url: AnyHttpUrl | str,
|
|
214
|
+
issuer_url: AnyHttpUrl | str | None = None,
|
|
213
215
|
redirect_path: str | None = None,
|
|
214
216
|
# Client configuration
|
|
215
217
|
allowed_client_redirect_uris: list[str] | None = None,
|
|
216
|
-
client_storage:
|
|
218
|
+
client_storage: AsyncKeyValue | None = None,
|
|
219
|
+
# JWT and encryption keys
|
|
220
|
+
jwt_signing_key: str | bytes | None = None,
|
|
217
221
|
# Token validation configuration
|
|
218
222
|
token_endpoint_auth_method: str | None = None,
|
|
223
|
+
# Consent screen configuration
|
|
224
|
+
require_authorization_consent: bool = True,
|
|
225
|
+
consent_csp_policy: str | None = None,
|
|
226
|
+
# Extra parameters
|
|
227
|
+
extra_authorize_params: dict[str, str] | None = None,
|
|
228
|
+
extra_token_params: dict[str, str] | None = None,
|
|
229
|
+
# Token expiry fallback
|
|
230
|
+
fallback_access_token_expiry_seconds: int | None = None,
|
|
219
231
|
) -> None:
|
|
220
232
|
"""Initialize the OIDC proxy provider.
|
|
221
233
|
|
|
@@ -226,21 +238,46 @@ class OIDCProxy(OAuthProxy):
|
|
|
226
238
|
client_secret: Client secret for upstream server
|
|
227
239
|
audience: Audience for upstream server
|
|
228
240
|
timeout_seconds: HTTP request timeout in seconds
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
241
|
+
token_verifier: Optional custom token verifier (e.g., IntrospectionTokenVerifier for opaque tokens).
|
|
242
|
+
If not provided, a JWTVerifier will be created using the OIDC configuration.
|
|
243
|
+
Cannot be used with algorithm or required_scopes parameters (configure these on your verifier instead).
|
|
244
|
+
algorithm: Token verifier algorithm (only used if token_verifier is not provided)
|
|
245
|
+
required_scopes: Required scopes for token validation (only used if token_verifier is not provided)
|
|
246
|
+
base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
|
|
247
|
+
issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
|
|
248
|
+
to avoid 404s during discovery when mounting under a path.
|
|
233
249
|
redirect_path: Redirect path configured in upstream OAuth app (defaults to "/auth/callback")
|
|
234
250
|
allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
|
|
235
251
|
Patterns support wildcards (e.g., "http://localhost:*", "https://*.example.com/*").
|
|
236
252
|
If None (default), only localhost redirect URIs are allowed.
|
|
237
253
|
If empty list, all redirect URIs are allowed (not recommended for production).
|
|
238
254
|
These are for MCP clients performing loopback redirects, NOT for the upstream OAuth app.
|
|
239
|
-
client_storage: Storage
|
|
240
|
-
|
|
255
|
+
client_storage: Storage backend for OAuth state (client registrations, encrypted tokens).
|
|
256
|
+
If None, a DiskStore will be created in the data directory (derived from `platformdirs`). The
|
|
257
|
+
disk store will be encrypted using a key derived from the JWT Signing Key.
|
|
258
|
+
jwt_signing_key: Secret for signing FastMCP JWT tokens (any string or bytes). If bytes are provided,
|
|
259
|
+
they will be used as is. If a string is provided, it will be derived into a 32-byte key. If not
|
|
260
|
+
provided, the upstream client secret will be used to derive a 32-byte key using PBKDF2.
|
|
241
261
|
token_endpoint_auth_method: Token endpoint authentication method for upstream server.
|
|
242
262
|
Common values: "client_secret_basic", "client_secret_post", "none".
|
|
243
263
|
If None, authlib will use its default (typically "client_secret_basic").
|
|
264
|
+
require_authorization_consent: Whether to require user consent before authorizing clients (default True).
|
|
265
|
+
When True, users see a consent screen before being redirected to the upstream IdP.
|
|
266
|
+
When False, authorization proceeds directly without user confirmation.
|
|
267
|
+
SECURITY WARNING: Only disable for local development or testing environments.
|
|
268
|
+
consent_csp_policy: Content Security Policy for the consent page.
|
|
269
|
+
If None (default), uses the built-in CSP policy with appropriate directives.
|
|
270
|
+
If empty string "", disables CSP entirely (no meta tag is rendered).
|
|
271
|
+
If a non-empty string, uses that as the CSP policy value.
|
|
272
|
+
extra_authorize_params: Additional parameters to forward to the upstream authorization endpoint.
|
|
273
|
+
Useful for provider-specific parameters like prompt=consent or access_type=offline.
|
|
274
|
+
Example: {"prompt": "consent", "access_type": "offline"}
|
|
275
|
+
extra_token_params: Additional parameters to forward to the upstream token endpoint.
|
|
276
|
+
Useful for provider-specific parameters during token exchange.
|
|
277
|
+
fallback_access_token_expiry_seconds: Expiry time to use when upstream provider
|
|
278
|
+
doesn't return `expires_in` in the token response. If not set, uses smart
|
|
279
|
+
defaults: 1 hour if a refresh token is available (since we can refresh),
|
|
280
|
+
or 1 year if no refresh token (for API-key-style tokens like GitHub OAuth Apps).
|
|
244
281
|
"""
|
|
245
282
|
if not config_url:
|
|
246
283
|
raise ValueError("Missing required config URL")
|
|
@@ -254,6 +291,19 @@ class OIDCProxy(OAuthProxy):
|
|
|
254
291
|
if not base_url:
|
|
255
292
|
raise ValueError("Missing required base URL")
|
|
256
293
|
|
|
294
|
+
# Validate that verifier-specific parameters are not used with custom verifier
|
|
295
|
+
if token_verifier is not None:
|
|
296
|
+
if algorithm is not None:
|
|
297
|
+
raise ValueError(
|
|
298
|
+
"Cannot specify 'algorithm' when providing a custom token_verifier. "
|
|
299
|
+
"Configure the algorithm on your token verifier instead."
|
|
300
|
+
)
|
|
301
|
+
if required_scopes is not None:
|
|
302
|
+
raise ValueError(
|
|
303
|
+
"Cannot specify 'required_scopes' when providing a custom token_verifier. "
|
|
304
|
+
"Configure required scopes on your token verifier instead."
|
|
305
|
+
)
|
|
306
|
+
|
|
257
307
|
if isinstance(config_url, str):
|
|
258
308
|
config_url = AnyHttpUrl(config_url)
|
|
259
309
|
|
|
@@ -273,12 +323,14 @@ class OIDCProxy(OAuthProxy):
|
|
|
273
323
|
else None
|
|
274
324
|
)
|
|
275
325
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
326
|
+
# Use custom verifier if provided, otherwise create default JWTVerifier
|
|
327
|
+
if token_verifier is None:
|
|
328
|
+
token_verifier = self.get_token_verifier(
|
|
329
|
+
algorithm=algorithm,
|
|
330
|
+
audience=audience,
|
|
331
|
+
required_scopes=required_scopes,
|
|
332
|
+
timeout_seconds=timeout_seconds,
|
|
333
|
+
)
|
|
282
334
|
|
|
283
335
|
init_kwargs = {
|
|
284
336
|
"upstream_authorization_endpoint": str(
|
|
@@ -290,21 +342,40 @@ class OIDCProxy(OAuthProxy):
|
|
|
290
342
|
"upstream_revocation_endpoint": revocation_endpoint,
|
|
291
343
|
"token_verifier": token_verifier,
|
|
292
344
|
"base_url": base_url,
|
|
345
|
+
"issuer_url": issuer_url or base_url,
|
|
293
346
|
"service_documentation_url": self.oidc_config.service_documentation,
|
|
294
347
|
"allowed_client_redirect_uris": allowed_client_redirect_uris,
|
|
295
348
|
"client_storage": client_storage,
|
|
349
|
+
"jwt_signing_key": jwt_signing_key,
|
|
296
350
|
"token_endpoint_auth_method": token_endpoint_auth_method,
|
|
351
|
+
"require_authorization_consent": require_authorization_consent,
|
|
352
|
+
"consent_csp_policy": consent_csp_policy,
|
|
353
|
+
"fallback_access_token_expiry_seconds": fallback_access_token_expiry_seconds,
|
|
297
354
|
}
|
|
298
355
|
|
|
299
356
|
if redirect_path:
|
|
300
357
|
init_kwargs["redirect_path"] = redirect_path
|
|
301
358
|
|
|
359
|
+
# Build extra params, merging audience with user-provided params
|
|
360
|
+
# User params override audience if there's a conflict
|
|
361
|
+
final_authorize_params: dict[str, str] = {}
|
|
362
|
+
final_token_params: dict[str, str] = {}
|
|
363
|
+
|
|
302
364
|
if audience:
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
365
|
+
final_authorize_params["audience"] = audience
|
|
366
|
+
final_token_params["audience"] = audience
|
|
367
|
+
|
|
368
|
+
if extra_authorize_params:
|
|
369
|
+
final_authorize_params.update(extra_authorize_params)
|
|
370
|
+
if extra_token_params:
|
|
371
|
+
final_token_params.update(extra_token_params)
|
|
372
|
+
|
|
373
|
+
if final_authorize_params:
|
|
374
|
+
init_kwargs["extra_authorize_params"] = final_authorize_params
|
|
375
|
+
if final_token_params:
|
|
376
|
+
init_kwargs["extra_token_params"] = final_token_params
|
|
306
377
|
|
|
307
|
-
super().__init__(**init_kwargs)
|
|
378
|
+
super().__init__(**init_kwargs) # ty: ignore[invalid-argument-type]
|
|
308
379
|
|
|
309
380
|
def get_oidc_configuration(
|
|
310
381
|
self,
|
|
@@ -21,13 +21,14 @@ Example:
|
|
|
21
21
|
```
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
+
from key_value.aio.protocols import AsyncKeyValue
|
|
24
25
|
from pydantic import AnyHttpUrl, SecretStr, field_validator
|
|
25
26
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
26
27
|
|
|
27
28
|
from fastmcp.server.auth.oidc_proxy import OIDCProxy
|
|
29
|
+
from fastmcp.settings import ENV_FILE
|
|
28
30
|
from fastmcp.utilities.auth import parse_scopes
|
|
29
31
|
from fastmcp.utilities.logging import get_logger
|
|
30
|
-
from fastmcp.utilities.storage import KVStorage
|
|
31
32
|
from fastmcp.utilities.types import NotSet, NotSetT
|
|
32
33
|
|
|
33
34
|
logger = get_logger(__name__)
|
|
@@ -38,7 +39,7 @@ class Auth0ProviderSettings(BaseSettings):
|
|
|
38
39
|
|
|
39
40
|
model_config = SettingsConfigDict(
|
|
40
41
|
env_prefix="FASTMCP_SERVER_AUTH_AUTH0_",
|
|
41
|
-
env_file=
|
|
42
|
+
env_file=ENV_FILE,
|
|
42
43
|
extra="ignore",
|
|
43
44
|
)
|
|
44
45
|
|
|
@@ -47,9 +48,11 @@ class Auth0ProviderSettings(BaseSettings):
|
|
|
47
48
|
client_secret: SecretStr | None = None
|
|
48
49
|
audience: str | None = None
|
|
49
50
|
base_url: AnyHttpUrl | None = None
|
|
51
|
+
issuer_url: AnyHttpUrl | None = None
|
|
50
52
|
redirect_path: str | None = None
|
|
51
53
|
required_scopes: list[str] | None = None
|
|
52
54
|
allowed_client_redirect_uris: list[str] | None = None
|
|
55
|
+
jwt_signing_key: str | None = None
|
|
53
56
|
|
|
54
57
|
@field_validator("required_scopes", mode="before")
|
|
55
58
|
@classmethod
|
|
@@ -89,10 +92,13 @@ class Auth0Provider(OIDCProxy):
|
|
|
89
92
|
client_secret: str | NotSetT = NotSet,
|
|
90
93
|
audience: str | NotSetT = NotSet,
|
|
91
94
|
base_url: AnyHttpUrl | str | NotSetT = NotSet,
|
|
95
|
+
issuer_url: AnyHttpUrl | str | NotSetT = NotSet,
|
|
92
96
|
required_scopes: list[str] | NotSetT = NotSet,
|
|
93
97
|
redirect_path: str | NotSetT = NotSet,
|
|
94
98
|
allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
|
|
95
|
-
client_storage:
|
|
99
|
+
client_storage: AsyncKeyValue | None = None,
|
|
100
|
+
jwt_signing_key: str | bytes | NotSetT = NotSet,
|
|
101
|
+
require_authorization_consent: bool = True,
|
|
96
102
|
) -> None:
|
|
97
103
|
"""Initialize Auth0 OAuth provider.
|
|
98
104
|
|
|
@@ -101,13 +107,23 @@ class Auth0Provider(OIDCProxy):
|
|
|
101
107
|
client_id: Auth0 application client id
|
|
102
108
|
client_secret: Auth0 application client secret
|
|
103
109
|
audience: Auth0 API audience
|
|
104
|
-
base_url: Public URL
|
|
110
|
+
base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
|
|
111
|
+
issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
|
|
112
|
+
to avoid 404s during discovery when mounting under a path.
|
|
105
113
|
required_scopes: Required Auth0 scopes (defaults to ["openid"])
|
|
106
114
|
redirect_path: Redirect path configured in Auth0 application
|
|
107
115
|
allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
|
|
108
116
|
If None (default), all URIs are allowed. If empty list, no URIs are allowed.
|
|
109
|
-
client_storage: Storage
|
|
110
|
-
|
|
117
|
+
client_storage: Storage backend for OAuth state (client registrations, encrypted tokens).
|
|
118
|
+
If None, a DiskStore will be created in the data directory (derived from `platformdirs`). The
|
|
119
|
+
disk store will be encrypted using a key derived from the JWT Signing Key.
|
|
120
|
+
jwt_signing_key: Secret for signing FastMCP JWT tokens (any string or bytes). If bytes are provided,
|
|
121
|
+
they will be used as is. If a string is provided, it will be derived into a 32-byte key. If not
|
|
122
|
+
provided, the upstream client secret will be used to derive a 32-byte key using PBKDF2.
|
|
123
|
+
require_authorization_consent: Whether to require user consent before authorizing clients (default True).
|
|
124
|
+
When True, users see a consent screen before being redirected to Auth0.
|
|
125
|
+
When False, authorization proceeds directly without user confirmation.
|
|
126
|
+
SECURITY WARNING: Only disable for local development or testing environments.
|
|
111
127
|
"""
|
|
112
128
|
settings = Auth0ProviderSettings.model_validate(
|
|
113
129
|
{
|
|
@@ -118,9 +134,11 @@ class Auth0Provider(OIDCProxy):
|
|
|
118
134
|
"client_secret": client_secret,
|
|
119
135
|
"audience": audience,
|
|
120
136
|
"base_url": base_url,
|
|
137
|
+
"issuer_url": issuer_url,
|
|
121
138
|
"required_scopes": required_scopes,
|
|
122
139
|
"redirect_path": redirect_path,
|
|
123
140
|
"allowed_client_redirect_uris": allowed_client_redirect_uris,
|
|
141
|
+
"jwt_signing_key": jwt_signing_key,
|
|
124
142
|
}.items()
|
|
125
143
|
if v is not NotSet
|
|
126
144
|
}
|
|
@@ -153,21 +171,22 @@ class Auth0Provider(OIDCProxy):
|
|
|
153
171
|
|
|
154
172
|
auth0_required_scopes = settings.required_scopes or ["openid"]
|
|
155
173
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
174
|
+
super().__init__(
|
|
175
|
+
config_url=settings.config_url,
|
|
176
|
+
client_id=settings.client_id,
|
|
177
|
+
client_secret=settings.client_secret.get_secret_value(),
|
|
178
|
+
audience=settings.audience,
|
|
179
|
+
base_url=settings.base_url,
|
|
180
|
+
issuer_url=settings.issuer_url,
|
|
181
|
+
redirect_path=settings.redirect_path,
|
|
182
|
+
required_scopes=auth0_required_scopes,
|
|
183
|
+
allowed_client_redirect_uris=settings.allowed_client_redirect_uris,
|
|
184
|
+
client_storage=client_storage,
|
|
185
|
+
jwt_signing_key=settings.jwt_signing_key,
|
|
186
|
+
require_authorization_consent=require_authorization_consent,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
logger.debug(
|
|
171
190
|
"Initialized Auth0 OAuth provider for client %s with scopes: %s",
|
|
172
191
|
settings.client_id,
|
|
173
192
|
auth0_required_scopes,
|
|
@@ -23,6 +23,7 @@ Example:
|
|
|
23
23
|
|
|
24
24
|
from __future__ import annotations
|
|
25
25
|
|
|
26
|
+
from key_value.aio.protocols import AsyncKeyValue
|
|
26
27
|
from pydantic import AnyHttpUrl, SecretStr, field_validator
|
|
27
28
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
28
29
|
|
|
@@ -30,6 +31,7 @@ from fastmcp.server.auth import TokenVerifier
|
|
|
30
31
|
from fastmcp.server.auth.auth import AccessToken
|
|
31
32
|
from fastmcp.server.auth.oidc_proxy import OIDCProxy
|
|
32
33
|
from fastmcp.server.auth.providers.jwt import JWTVerifier
|
|
34
|
+
from fastmcp.settings import ENV_FILE
|
|
33
35
|
from fastmcp.utilities.auth import parse_scopes
|
|
34
36
|
from fastmcp.utilities.logging import get_logger
|
|
35
37
|
from fastmcp.utilities.types import NotSet, NotSetT
|
|
@@ -42,7 +44,7 @@ class AWSCognitoProviderSettings(BaseSettings):
|
|
|
42
44
|
|
|
43
45
|
model_config = SettingsConfigDict(
|
|
44
46
|
env_prefix="FASTMCP_SERVER_AUTH_AWS_COGNITO_",
|
|
45
|
-
env_file=
|
|
47
|
+
env_file=ENV_FILE,
|
|
46
48
|
extra="ignore",
|
|
47
49
|
)
|
|
48
50
|
|
|
@@ -51,9 +53,11 @@ class AWSCognitoProviderSettings(BaseSettings):
|
|
|
51
53
|
client_id: str | None = None
|
|
52
54
|
client_secret: SecretStr | None = None
|
|
53
55
|
base_url: AnyHttpUrl | str | None = None
|
|
56
|
+
issuer_url: AnyHttpUrl | str | None = None
|
|
54
57
|
redirect_path: str | None = None
|
|
55
58
|
required_scopes: list[str] | None = None
|
|
56
59
|
allowed_client_redirect_uris: list[str] | None = None
|
|
60
|
+
jwt_signing_key: str | None = None
|
|
57
61
|
|
|
58
62
|
@field_validator("required_scopes", mode="before")
|
|
59
63
|
@classmethod
|
|
@@ -127,9 +131,13 @@ class AWSCognitoProvider(OIDCProxy):
|
|
|
127
131
|
client_id: str | NotSetT = NotSet,
|
|
128
132
|
client_secret: str | NotSetT = NotSet,
|
|
129
133
|
base_url: AnyHttpUrl | str | NotSetT = NotSet,
|
|
134
|
+
issuer_url: AnyHttpUrl | str | NotSetT = NotSet,
|
|
130
135
|
redirect_path: str | NotSetT = NotSet,
|
|
131
136
|
required_scopes: list[str] | NotSetT = NotSet,
|
|
132
137
|
allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
|
|
138
|
+
client_storage: AsyncKeyValue | None = None,
|
|
139
|
+
jwt_signing_key: str | bytes | NotSetT = NotSet,
|
|
140
|
+
require_authorization_consent: bool = True,
|
|
133
141
|
):
|
|
134
142
|
"""Initialize AWS Cognito OAuth provider.
|
|
135
143
|
|
|
@@ -138,11 +146,23 @@ class AWSCognitoProvider(OIDCProxy):
|
|
|
138
146
|
aws_region: AWS region where your User Pool is located (defaults to "eu-central-1")
|
|
139
147
|
client_id: Cognito app client ID
|
|
140
148
|
client_secret: Cognito app client secret
|
|
141
|
-
base_url: Public URL
|
|
149
|
+
base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
|
|
150
|
+
issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
|
|
151
|
+
to avoid 404s during discovery when mounting under a path.
|
|
142
152
|
redirect_path: Redirect path configured in Cognito app (defaults to "/auth/callback")
|
|
143
153
|
required_scopes: Required Cognito scopes (defaults to ["openid"])
|
|
144
154
|
allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
|
|
145
155
|
If None (default), all URIs are allowed. If empty list, no URIs are allowed.
|
|
156
|
+
client_storage: Storage backend for OAuth state (client registrations, encrypted tokens).
|
|
157
|
+
If None, a DiskStore will be created in the data directory (derived from `platformdirs`). The
|
|
158
|
+
disk store will be encrypted using a key derived from the JWT Signing Key.
|
|
159
|
+
jwt_signing_key: Secret for signing FastMCP JWT tokens (any string or bytes). If bytes are provided,
|
|
160
|
+
they will be used as is. If a string is provided, it will be derived into a 32-byte key. If not
|
|
161
|
+
provided, the upstream client secret will be used to derive a 32-byte key using PBKDF2.
|
|
162
|
+
require_authorization_consent: Whether to require user consent before authorizing clients (default True).
|
|
163
|
+
When True, users see a consent screen before being redirected to AWS Cognito.
|
|
164
|
+
When False, authorization proceeds directly without user confirmation.
|
|
165
|
+
SECURITY WARNING: Only disable for local development or testing environments.
|
|
146
166
|
"""
|
|
147
167
|
|
|
148
168
|
settings = AWSCognitoProviderSettings.model_validate(
|
|
@@ -154,9 +174,11 @@ class AWSCognitoProvider(OIDCProxy):
|
|
|
154
174
|
"client_id": client_id,
|
|
155
175
|
"client_secret": client_secret,
|
|
156
176
|
"base_url": base_url,
|
|
177
|
+
"issuer_url": issuer_url,
|
|
157
178
|
"redirect_path": redirect_path,
|
|
158
179
|
"required_scopes": required_scopes,
|
|
159
180
|
"allowed_client_redirect_uris": allowed_client_redirect_uris,
|
|
181
|
+
"jwt_signing_key": jwt_signing_key,
|
|
160
182
|
}.items()
|
|
161
183
|
if v is not NotSet
|
|
162
184
|
}
|
|
@@ -202,11 +224,15 @@ class AWSCognitoProvider(OIDCProxy):
|
|
|
202
224
|
algorithm="RS256",
|
|
203
225
|
required_scopes=required_scopes_final,
|
|
204
226
|
base_url=settings.base_url,
|
|
227
|
+
issuer_url=settings.issuer_url,
|
|
205
228
|
redirect_path=redirect_path_final,
|
|
206
229
|
allowed_client_redirect_uris=allowed_client_redirect_uris_final,
|
|
230
|
+
client_storage=client_storage,
|
|
231
|
+
jwt_signing_key=settings.jwt_signing_key,
|
|
232
|
+
require_authorization_consent=require_authorization_consent,
|
|
207
233
|
)
|
|
208
234
|
|
|
209
|
-
logger.
|
|
235
|
+
logger.debug(
|
|
210
236
|
"Initialized AWS Cognito OAuth provider for client %s with scopes: %s",
|
|
211
237
|
settings.client_id,
|
|
212
238
|
required_scopes_final,
|