fastmcp 2.12.2__py3-none-any.whl → 2.12.4__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.
Files changed (54) hide show
  1. fastmcp/cli/claude.py +1 -10
  2. fastmcp/cli/cli.py +45 -25
  3. fastmcp/cli/install/__init__.py +2 -0
  4. fastmcp/cli/install/claude_code.py +1 -10
  5. fastmcp/cli/install/claude_desktop.py +1 -9
  6. fastmcp/cli/install/cursor.py +2 -18
  7. fastmcp/cli/install/gemini_cli.py +241 -0
  8. fastmcp/cli/install/mcp_json.py +1 -9
  9. fastmcp/cli/run.py +2 -86
  10. fastmcp/client/auth/oauth.py +50 -37
  11. fastmcp/client/client.py +18 -8
  12. fastmcp/client/elicitation.py +6 -1
  13. fastmcp/client/transports.py +1 -1
  14. fastmcp/contrib/component_manager/component_service.py +1 -1
  15. fastmcp/contrib/mcp_mixin/README.md +3 -3
  16. fastmcp/contrib/mcp_mixin/mcp_mixin.py +41 -6
  17. fastmcp/experimental/utilities/openapi/director.py +8 -1
  18. fastmcp/experimental/utilities/openapi/schemas.py +31 -5
  19. fastmcp/prompts/prompt.py +10 -8
  20. fastmcp/resources/resource.py +14 -11
  21. fastmcp/resources/template.py +12 -10
  22. fastmcp/server/auth/auth.py +10 -4
  23. fastmcp/server/auth/oauth_proxy.py +93 -23
  24. fastmcp/server/auth/oidc_proxy.py +348 -0
  25. fastmcp/server/auth/providers/auth0.py +174 -0
  26. fastmcp/server/auth/providers/aws.py +237 -0
  27. fastmcp/server/auth/providers/azure.py +6 -2
  28. fastmcp/server/auth/providers/descope.py +172 -0
  29. fastmcp/server/auth/providers/github.py +6 -2
  30. fastmcp/server/auth/providers/google.py +6 -2
  31. fastmcp/server/auth/providers/workos.py +6 -2
  32. fastmcp/server/context.py +17 -16
  33. fastmcp/server/dependencies.py +18 -5
  34. fastmcp/server/http.py +1 -1
  35. fastmcp/server/middleware/logging.py +147 -116
  36. fastmcp/server/middleware/middleware.py +3 -2
  37. fastmcp/server/openapi.py +5 -1
  38. fastmcp/server/server.py +43 -36
  39. fastmcp/settings.py +42 -6
  40. fastmcp/tools/tool.py +105 -87
  41. fastmcp/tools/tool_transform.py +1 -1
  42. fastmcp/utilities/json_schema.py +18 -1
  43. fastmcp/utilities/logging.py +66 -4
  44. fastmcp/utilities/mcp_server_config/v1/environments/uv.py +4 -39
  45. fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +3 -2
  46. fastmcp/utilities/mcp_server_config/v1/schema.json +2 -1
  47. fastmcp/utilities/storage.py +204 -0
  48. fastmcp/utilities/tests.py +8 -6
  49. fastmcp/utilities/types.py +9 -5
  50. {fastmcp-2.12.2.dist-info → fastmcp-2.12.4.dist-info}/METADATA +121 -48
  51. {fastmcp-2.12.2.dist-info → fastmcp-2.12.4.dist-info}/RECORD +54 -48
  52. {fastmcp-2.12.2.dist-info → fastmcp-2.12.4.dist-info}/WHEEL +0 -0
  53. {fastmcp-2.12.2.dist-info → fastmcp-2.12.4.dist-info}/entry_points.txt +0 -0
  54. {fastmcp-2.12.2.dist-info → fastmcp-2.12.4.dist-info}/licenses/LICENSE +0 -0
@@ -45,9 +45,11 @@ from starlette.requests import Request
45
45
  from starlette.responses import RedirectResponse
46
46
  from starlette.routing import Route
47
47
 
48
+ import fastmcp
48
49
  from fastmcp.server.auth.auth import OAuthProvider, TokenVerifier
49
50
  from fastmcp.server.auth.redirect_validation import validate_redirect_uri
50
51
  from fastmcp.utilities.logging import get_logger
52
+ from fastmcp.utilities.storage import JSONFileStorage, KVStorage
51
53
 
52
54
  if TYPE_CHECKING:
53
55
  pass
@@ -240,7 +242,7 @@ class OAuthProxy(OAuthProvider):
240
242
  token_verifier: TokenVerifier,
241
243
  # FastMCP server configuration
242
244
  base_url: AnyHttpUrl | str,
243
- redirect_path: str = "/auth/callback",
245
+ redirect_path: str | None = None,
244
246
  issuer_url: AnyHttpUrl | str | None = None,
245
247
  service_documentation_url: AnyHttpUrl | str | None = None,
246
248
  # Client redirect URI validation
@@ -250,6 +252,12 @@ class OAuthProxy(OAuthProvider):
250
252
  forward_pkce: bool = True,
251
253
  # Token endpoint authentication
252
254
  token_endpoint_auth_method: str | None = None,
255
+ # Extra parameters to forward to authorization endpoint
256
+ extra_authorize_params: dict[str, str] | None = None,
257
+ # Extra parameters to forward to token endpoint
258
+ extra_token_params: dict[str, str] | None = None,
259
+ # Client storage
260
+ client_storage: KVStorage | None = None,
253
261
  ):
254
262
  """Initialize the OAuth proxy provider.
255
263
 
@@ -273,11 +281,19 @@ class OAuthProxy(OAuthProvider):
273
281
  valid_scopes: List of all the possible valid scopes for a client.
274
282
  These are advertised to clients through the `/.well-known` endpoints. Defaults to `required_scopes` if not provided.
275
283
  forward_pkce: Whether to forward PKCE to upstream server (default True).
276
- Enable for providers that support/require PKCE (Google, Azure, etc.).
284
+ Enable for providers that support/require PKCE (Google, Azure, AWS, etc.).
277
285
  Disable only if upstream provider doesn't support PKCE.
278
286
  token_endpoint_auth_method: Token endpoint authentication method for upstream server.
279
287
  Common values: "client_secret_basic", "client_secret_post", "none".
280
288
  If None, authlib will use its default (typically "client_secret_basic").
289
+ extra_authorize_params: Additional parameters to forward to the upstream authorization endpoint.
290
+ Useful for provider-specific parameters like Auth0's "audience".
291
+ Example: {"audience": "https://api.example.com"}
292
+ extra_token_params: Additional parameters to forward to the upstream token endpoint.
293
+ Useful for provider-specific parameters during token exchange.
294
+ client_storage: Storage implementation for OAuth client registrations.
295
+ Defaults to file-based storage in ~/.fastmcp/oauth-proxy-clients/ if not specified.
296
+ Pass any KVStorage implementation for custom storage backends.
281
297
  """
282
298
  # Always enable DCR since we implement it locally for MCP clients
283
299
  client_registration_options = ClientRegistrationOptions(
@@ -308,9 +324,12 @@ class OAuthProxy(OAuthProvider):
308
324
  self._default_scope_str = " ".join(self.required_scopes or [])
309
325
 
310
326
  # Store redirect configuration
311
- self._redirect_path = (
312
- redirect_path if redirect_path.startswith("/") else f"/{redirect_path}"
313
- )
327
+ if not redirect_path:
328
+ self._redirect_path = "/auth/callback"
329
+ else:
330
+ self._redirect_path = (
331
+ redirect_path if redirect_path.startswith("/") else f"/{redirect_path}"
332
+ )
314
333
  self._allowed_client_redirect_uris = allowed_client_redirect_uris
315
334
 
316
335
  # PKCE configuration
@@ -319,8 +338,17 @@ class OAuthProxy(OAuthProvider):
319
338
  # Token endpoint authentication
320
339
  self._token_endpoint_auth_method = token_endpoint_auth_method
321
340
 
322
- # Local state for DCR and token bookkeeping
323
- self._clients: dict[str, OAuthClientInformationFull] = {}
341
+ # Extra parameters for authorization and token endpoints
342
+ self._extra_authorize_params = extra_authorize_params or {}
343
+ self._extra_token_params = extra_token_params or {}
344
+
345
+ # Initialize client storage (default to file-based if not provided)
346
+ if client_storage is None:
347
+ cache_dir = fastmcp.settings.home / "oauth-proxy-clients"
348
+ client_storage = JSONFileStorage(cache_dir)
349
+ self._client_storage = client_storage
350
+
351
+ # Local state for token bookkeeping only (no client caching)
324
352
  self._access_tokens: dict[str, AccessToken] = {}
325
353
  self._refresh_tokens: dict[str, RefreshToken] = {}
326
354
 
@@ -371,9 +399,20 @@ class OAuthProxy(OAuthProvider):
371
399
 
372
400
  For unregistered clients, returns None (which will raise an error in the SDK).
373
401
  """
374
- client = self._clients.get(client_id)
402
+ # Load from storage
403
+ data = await self._client_storage.get(client_id)
404
+ if not data:
405
+ return None
375
406
 
376
- return client
407
+ if client_data := data.get("client", None):
408
+ return ProxyDCRClient(
409
+ allowed_redirect_uri_patterns=data.get(
410
+ "allowed_redirect_uri_patterns", self._allowed_client_redirect_uris
411
+ ),
412
+ **client_data,
413
+ )
414
+
415
+ return None
377
416
 
378
417
  async def register_client(self, client_info: OAuthClientInformationFull) -> None:
379
418
  """Register a client locally
@@ -391,13 +430,17 @@ class OAuthProxy(OAuthProvider):
391
430
  redirect_uris=client_info.redirect_uris or [AnyUrl("http://localhost")],
392
431
  grant_types=client_info.grant_types
393
432
  or ["authorization_code", "refresh_token"],
394
- scope=self._default_scope_str,
433
+ scope=client_info.scope or self._default_scope_str,
395
434
  token_endpoint_auth_method="none",
396
435
  allowed_redirect_uri_patterns=self._allowed_client_redirect_uris,
397
436
  )
398
437
 
399
- # Store the ProxyDCRClient
400
- self._clients[client_info.client_id] = proxy_client
438
+ # Store as structured dict with all needed metadata
439
+ storage_data = {
440
+ "client": proxy_client.model_dump(mode="json"),
441
+ "allowed_redirect_uri_patterns": self._allowed_client_redirect_uris,
442
+ }
443
+ await self._client_storage.set(client_info.client_id, storage_data)
401
444
 
402
445
  # Log redirect URIs to help users discover what patterns they might need
403
446
  if client_info.redirect_uris:
@@ -485,6 +528,24 @@ class OAuthProxy(OAuthProvider):
485
528
  txn_id,
486
529
  )
487
530
 
531
+ # Forward resource parameter if provided (RFC 8707)
532
+ if params.resource:
533
+ query_params["resource"] = params.resource
534
+ logger.debug(
535
+ "Forwarding resource indicator '%s' to upstream for transaction %s",
536
+ params.resource,
537
+ txn_id,
538
+ )
539
+
540
+ # Add any extra authorization parameters configured for this proxy
541
+ if self._extra_authorize_params:
542
+ query_params.update(self._extra_authorize_params)
543
+ logger.debug(
544
+ "Adding extra authorization parameters for transaction %s: %s",
545
+ txn_id,
546
+ list(self._extra_authorize_params.keys()),
547
+ )
548
+
488
549
  # Build the upstream authorization URL
489
550
  separator = "&" if "?" in self._upstream_authorization_endpoint else "?"
490
551
  upstream_url = f"{self._upstream_authorization_endpoint}{separator}{urlencode(query_params)}"
@@ -870,26 +931,35 @@ class OAuthProxy(OAuthProvider):
870
931
  f"Exchanging IdP code for tokens with redirect_uri: {idp_redirect_uri}"
871
932
  )
872
933
 
934
+ # Build token exchange parameters
935
+ token_params = {
936
+ "url": self._upstream_token_endpoint,
937
+ "code": idp_code,
938
+ "redirect_uri": idp_redirect_uri,
939
+ }
940
+
873
941
  # Include proxy's code_verifier if we forwarded PKCE
874
942
  proxy_code_verifier = transaction.get("proxy_code_verifier")
875
943
  if proxy_code_verifier:
944
+ token_params["code_verifier"] = proxy_code_verifier
876
945
  logger.debug(
877
946
  "Including proxy code_verifier in token exchange for transaction %s",
878
947
  txn_id,
879
948
  )
880
- idp_tokens: dict[str, Any] = await oauth_client.fetch_token( # type: ignore[misc]
881
- url=self._upstream_token_endpoint,
882
- code=idp_code,
883
- redirect_uri=idp_redirect_uri,
884
- code_verifier=proxy_code_verifier,
885
- )
886
- else:
887
- idp_tokens: dict[str, Any] = await oauth_client.fetch_token( # type: ignore[misc]
888
- url=self._upstream_token_endpoint,
889
- code=idp_code,
890
- redirect_uri=idp_redirect_uri,
949
+
950
+ # Add any extra token parameters configured for this proxy
951
+ if self._extra_token_params:
952
+ token_params.update(self._extra_token_params)
953
+ logger.debug(
954
+ "Adding extra token parameters for transaction %s: %s",
955
+ txn_id,
956
+ list(self._extra_token_params.keys()),
891
957
  )
892
958
 
959
+ idp_tokens: dict[str, Any] = await oauth_client.fetch_token(
960
+ **token_params
961
+ ) # type: ignore[misc]
962
+
893
963
  logger.debug(
894
964
  f"Successfully exchanged IdP code for tokens (transaction: {txn_id}, PKCE: {bool(proxy_code_verifier)})"
895
965
  )
@@ -0,0 +1,348 @@
1
+ """OIDC Proxy Provider for FastMCP.
2
+
3
+ This provider acts as a transparent proxy to an upstream OIDC compliant Authorization
4
+ Server. It leverages the OAuthProxy class to handle Dynamic Client Registration and
5
+ forwarding of all OAuth flows.
6
+
7
+ This implementation is based on:
8
+ OpenID Connect Discovery 1.0 - https://openid.net/specs/openid-connect-discovery-1_0.html
9
+ OAuth 2.0 Authorization Server Metadata - https://datatracker.ietf.org/doc/html/rfc8414
10
+ """
11
+
12
+ from collections.abc import Sequence
13
+
14
+ import httpx
15
+ from pydantic import AnyHttpUrl, BaseModel, model_validator
16
+ from typing_extensions import Self
17
+
18
+ from fastmcp.server.auth import TokenVerifier
19
+ from fastmcp.server.auth.oauth_proxy import OAuthProxy
20
+ from fastmcp.server.auth.providers.jwt import JWTVerifier
21
+ from fastmcp.utilities.logging import get_logger
22
+ from fastmcp.utilities.storage import KVStorage
23
+
24
+ logger = get_logger(__name__)
25
+
26
+
27
+ class OIDCConfiguration(BaseModel):
28
+ """OIDC Configuration.
29
+
30
+ See:
31
+ https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
32
+ https://datatracker.ietf.org/doc/html/rfc8414#section-2
33
+ """
34
+
35
+ strict: bool = True
36
+
37
+ # OpenID Connect Discovery 1.0
38
+ issuer: AnyHttpUrl | str | None = None # Strict
39
+
40
+ authorization_endpoint: AnyHttpUrl | str | None = None # Strict
41
+ token_endpoint: AnyHttpUrl | str | None = None # Strict
42
+ userinfo_endpoint: AnyHttpUrl | str | None = None
43
+
44
+ jwks_uri: AnyHttpUrl | str | None = None # Strict
45
+
46
+ registration_endpoint: AnyHttpUrl | str | None = None
47
+
48
+ scopes_supported: Sequence[str] | None = None
49
+
50
+ response_types_supported: Sequence[str] | None = None # Strict
51
+ response_modes_supported: Sequence[str] | None = None
52
+
53
+ grant_types_supported: Sequence[str] | None = None
54
+
55
+ acr_values_supported: Sequence[str] | None = None
56
+
57
+ subject_types_supported: Sequence[str] | None = None # Strict
58
+
59
+ id_token_signing_alg_values_supported: Sequence[str] | None = None # Strict
60
+ id_token_encryption_alg_values_supported: Sequence[str] | None = None
61
+ id_token_encryption_enc_values_supported: Sequence[str] | None = None
62
+
63
+ userinfo_signing_alg_values_supported: Sequence[str] | None = None
64
+ userinfo_encryption_alg_values_supported: Sequence[str] | None = None
65
+ userinfo_encryption_enc_values_supported: Sequence[str] | None = None
66
+
67
+ request_object_signing_alg_values_supported: Sequence[str] | None = None
68
+ request_object_encryption_alg_values_supported: Sequence[str] | None = None
69
+ request_object_encryption_enc_values_supported: Sequence[str] | None = None
70
+
71
+ token_endpoint_auth_methods_supported: Sequence[str] | None = None
72
+ token_endpoint_auth_signing_alg_values_supported: Sequence[str] | None = None
73
+
74
+ display_values_supported: Sequence[str] | None = None
75
+
76
+ claim_types_supported: Sequence[str] | None = None
77
+ claims_supported: Sequence[str] | None = None
78
+
79
+ service_documentation: AnyHttpUrl | str | None = None
80
+
81
+ claims_locales_supported: Sequence[str] | None = None
82
+ ui_locales_supported: Sequence[str] | None = None
83
+
84
+ claims_parameter_supported: bool | None = None
85
+ request_parameter_supported: bool | None = None
86
+ request_uri_parameter_supported: bool | None = None
87
+
88
+ require_request_uri_registration: bool | None = None
89
+
90
+ op_policy_uri: AnyHttpUrl | str | None = None
91
+ op_tos_uri: AnyHttpUrl | str | None = None
92
+
93
+ # OAuth 2.0 Authorization Server Metadata
94
+ revocation_endpoint: AnyHttpUrl | str | None = None
95
+ revocation_endpoint_auth_methods_supported: Sequence[str] | None = None
96
+ revocation_endpoint_auth_signing_alg_values_supported: Sequence[str] | None = None
97
+
98
+ introspection_endpoint: AnyHttpUrl | str | None = None
99
+ introspection_endpoint_auth_methods_supported: Sequence[str] | None = None
100
+ introspection_endpoint_auth_signing_alg_values_supported: Sequence[str] | None = (
101
+ None
102
+ )
103
+
104
+ code_challenge_methods_supported: Sequence[str] | None = None
105
+
106
+ signed_metadata: str | None = None
107
+
108
+ @model_validator(mode="after")
109
+ def _enforce_strict(self) -> Self:
110
+ """Enforce strict rules."""
111
+ if not self.strict:
112
+ return self
113
+
114
+ def enforce(attr: str, is_url: bool = False) -> None:
115
+ value = getattr(self, attr, None)
116
+ if not value:
117
+ message = f"Missing required configuration metadata: {attr}"
118
+ logger.error(message)
119
+ raise ValueError(message)
120
+
121
+ if not is_url or isinstance(value, AnyHttpUrl):
122
+ return
123
+
124
+ try:
125
+ AnyHttpUrl(value)
126
+ except Exception:
127
+ message = f"Invalid URL for configuration metadata: {attr}"
128
+ logger.error(message)
129
+ raise ValueError(message)
130
+
131
+ enforce("issuer", True)
132
+ enforce("authorization_endpoint", True)
133
+ enforce("token_endpoint", True)
134
+ enforce("jwks_uri", True)
135
+ enforce("response_types_supported")
136
+ enforce("subject_types_supported")
137
+ enforce("id_token_signing_alg_values_supported")
138
+
139
+ return self
140
+
141
+ @classmethod
142
+ def get_oidc_configuration(
143
+ cls, config_url: AnyHttpUrl, *, strict: bool | None, timeout_seconds: int | None
144
+ ) -> Self:
145
+ """Get the OIDC configuration for the specified config URL.
146
+
147
+ Args:
148
+ config_url: The OIDC config URL
149
+ strict: The strict flag for the configuration
150
+ timeout_seconds: HTTP request timeout in seconds
151
+ """
152
+ get_kwargs = {}
153
+ if timeout_seconds is not None:
154
+ get_kwargs["timeout"] = timeout_seconds
155
+
156
+ try:
157
+ response = httpx.get(str(config_url), **get_kwargs)
158
+ response.raise_for_status()
159
+
160
+ config_data = response.json()
161
+ if strict is not None:
162
+ config_data["strict"] = strict
163
+
164
+ return cls.model_validate(config_data)
165
+ except Exception:
166
+ logger.exception(
167
+ f"Unable to get OIDC configuration for config url: {config_url}"
168
+ )
169
+ raise
170
+
171
+
172
+ class OIDCProxy(OAuthProxy):
173
+ """OAuth provider that wraps OAuthProxy to provide configuration via an OIDC configuration URL.
174
+
175
+ This provider makes it easier to add OAuth protection for any upstream provider
176
+ that is OIDC compliant.
177
+
178
+ Example:
179
+ ```python
180
+ from fastmcp import FastMCP
181
+ from fastmcp.server.auth.oidc_proxy import OIDCProxy
182
+
183
+ # Simple OIDC based protection
184
+ auth = OIDCProxy(
185
+ config_url="https://oidc.config.url",
186
+ client_id="your-oidc-client-id",
187
+ client_secret="your-oidc-client-secret",
188
+ base_url="https://your.server.url",
189
+ )
190
+
191
+ mcp = FastMCP("My Protected Server", auth=auth)
192
+ ```
193
+ """
194
+
195
+ oidc_config: OIDCConfiguration
196
+
197
+ def __init__(
198
+ self,
199
+ *,
200
+ # OIDC configuration
201
+ config_url: AnyHttpUrl | str,
202
+ strict: bool | None = None,
203
+ # Upstream server configuration
204
+ client_id: str,
205
+ client_secret: str,
206
+ audience: str | None = None,
207
+ timeout_seconds: int | None = None,
208
+ # Token verifier
209
+ algorithm: str | None = None,
210
+ required_scopes: list[str] | None = None,
211
+ # FastMCP server configuration
212
+ base_url: AnyHttpUrl | str,
213
+ redirect_path: str | None = None,
214
+ # Client configuration
215
+ allowed_client_redirect_uris: list[str] | None = None,
216
+ client_storage: KVStorage | None = None,
217
+ # Token validation configuration
218
+ token_endpoint_auth_method: str | None = None,
219
+ ) -> None:
220
+ """Initialize the OIDC proxy provider.
221
+
222
+ Args:
223
+ config_url: URL of upstream configuration
224
+ strict: Optional strict flag for the configuration
225
+ client_id: Client ID registered with upstream server
226
+ client_secret: Client secret for upstream server
227
+ audience: Audience for upstream server
228
+ timeout_seconds: HTTP request timeout in seconds
229
+ algorithm: Token verifier algorithm
230
+ required_scopes: Required OAuth scopes
231
+ base_url: Public URL of the server that exposes this FastMCP server; redirect path is
232
+ relative to this URL
233
+ redirect_path: Redirect path configured in upstream OAuth app (defaults to "/auth/callback")
234
+ allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
235
+ Patterns support wildcards (e.g., "http://localhost:*", "https://*.example.com/*").
236
+ If None (default), only localhost redirect URIs are allowed.
237
+ If empty list, all redirect URIs are allowed (not recommended for production).
238
+ These are for MCP clients performing loopback redirects, NOT for the upstream OAuth app.
239
+ client_storage: Storage implementation for OAuth client registrations.
240
+ Defaults to file-based storage if not specified.
241
+ token_endpoint_auth_method: Token endpoint authentication method for upstream server.
242
+ Common values: "client_secret_basic", "client_secret_post", "none".
243
+ If None, authlib will use its default (typically "client_secret_basic").
244
+ """
245
+ if not config_url:
246
+ raise ValueError("Missing required config URL")
247
+
248
+ if not client_id:
249
+ raise ValueError("Missing required client id")
250
+
251
+ if not client_secret:
252
+ raise ValueError("Missing required client secret")
253
+
254
+ if not base_url:
255
+ raise ValueError("Missing required base URL")
256
+
257
+ if isinstance(config_url, str):
258
+ config_url = AnyHttpUrl(config_url)
259
+
260
+ self.oidc_config = self.get_oidc_configuration(
261
+ config_url, strict, timeout_seconds
262
+ )
263
+ if (
264
+ not self.oidc_config.authorization_endpoint
265
+ or not self.oidc_config.token_endpoint
266
+ ):
267
+ logger.debug(f"Invalid OIDC Configuration: {self.oidc_config}")
268
+ raise ValueError("Missing required OIDC endpoints")
269
+
270
+ revocation_endpoint = (
271
+ str(self.oidc_config.revocation_endpoint)
272
+ if self.oidc_config.revocation_endpoint
273
+ else None
274
+ )
275
+
276
+ token_verifier = self.get_token_verifier(
277
+ algorithm=algorithm,
278
+ audience=audience,
279
+ required_scopes=required_scopes,
280
+ timeout_seconds=timeout_seconds,
281
+ )
282
+
283
+ init_kwargs = {
284
+ "upstream_authorization_endpoint": str(
285
+ self.oidc_config.authorization_endpoint
286
+ ),
287
+ "upstream_token_endpoint": str(self.oidc_config.token_endpoint),
288
+ "upstream_client_id": client_id,
289
+ "upstream_client_secret": client_secret,
290
+ "upstream_revocation_endpoint": revocation_endpoint,
291
+ "token_verifier": token_verifier,
292
+ "base_url": base_url,
293
+ "service_documentation_url": self.oidc_config.service_documentation,
294
+ "allowed_client_redirect_uris": allowed_client_redirect_uris,
295
+ "client_storage": client_storage,
296
+ "token_endpoint_auth_method": token_endpoint_auth_method,
297
+ }
298
+
299
+ if redirect_path:
300
+ init_kwargs["redirect_path"] = redirect_path
301
+
302
+ if audience:
303
+ extra_params = {"audience": audience}
304
+ init_kwargs["extra_authorize_params"] = extra_params
305
+ init_kwargs["extra_token_params"] = extra_params
306
+
307
+ super().__init__(**init_kwargs)
308
+
309
+ def get_oidc_configuration(
310
+ self,
311
+ config_url: AnyHttpUrl,
312
+ strict: bool | None,
313
+ timeout_seconds: int | None,
314
+ ) -> OIDCConfiguration:
315
+ """Gets the OIDC configuration for the specified configuration URL.
316
+
317
+ Args:
318
+ config_url: The OIDC configuration URL
319
+ strict: The strict flag for the configuration
320
+ timeout_seconds: HTTP request timeout in seconds
321
+ """
322
+ return OIDCConfiguration.get_oidc_configuration(
323
+ config_url, strict=strict, timeout_seconds=timeout_seconds
324
+ )
325
+
326
+ def get_token_verifier(
327
+ self,
328
+ *,
329
+ algorithm: str | None = None,
330
+ audience: str | None = None,
331
+ required_scopes: list[str] | None = None,
332
+ timeout_seconds: int | None = None,
333
+ ) -> TokenVerifier:
334
+ """Creates the token verifier for the specified OIDC configuration and arguments.
335
+
336
+ Args:
337
+ algorithm: Optional token verifier algorithm
338
+ audience: Optional token verifier audience
339
+ required_scopes: Optional token verifier required_scopes
340
+ timeout_seconds: HTTP request timeout in seconds
341
+ """
342
+ return JWTVerifier(
343
+ jwks_uri=str(self.oidc_config.jwks_uri),
344
+ issuer=str(self.oidc_config.issuer),
345
+ algorithm=algorithm,
346
+ audience=audience,
347
+ required_scopes=required_scopes,
348
+ )