### Install Dependencies Source: https://github.com/auth0/auth0-api-python/blob/main/CONTRIBUTING.md Installs project dependencies using Poetry. Ensure Poetry is installed before running. ```bash poetry install ``` -------------------------------- ### Install Auth0 API Python SDK Source: https://github.com/auth0/auth0-api-python/blob/main/README.md Install the SDK using pip or Poetry. Requires Python 3.9+. ```shell pip install auth0-api-python ``` ```shell poetry install auth0-api-python ``` -------------------------------- ### RedisCache Implementation Example Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md An example implementation of a custom cache adapter using Redis. This class demonstrates how to fulfill the CacheAdapter interface using the redis-py library. ```APIDOC **Example (Redis):** ```python import json import redis from auth0_api_python import CacheAdapter class RedisCache(CacheAdapter): def __init__(self, redis_client): self.redis = redis_client def get(self, key: str): value = self.redis.get(key) return json.loads(value) if value else None def set(self, key: str, value, ttl_seconds=None): self.redis.set(key, json.dumps(value), ex=ttl_seconds) def delete(self, key: str): self.redis.delete(key) def clear(self): self.redis.flushdb() # Usage redis_client = redis.Redis() cache = RedisCache(redis_client) api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com", cache_adapter=cache )) ``` ``` -------------------------------- ### DomainsResolver Example Usage Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/types.md Demonstrates how to use the DomainsResolverContext to dynamically resolve allowed domains based on request details. This example shows a simple host-based resolution. ```python def resolve_domains(context: DomainsResolverContext) -> list[str]: host = (context.get('request_headers') or {}).get('host') if host == 'api.brand.com': return ['brand.custom-domain.com'] return ['tenant.auth0.com'] api_client = ApiClient(ApiClientOptions( domains=resolve_domains, audience="https://api.example.com" )) ``` -------------------------------- ### Example Usage of OnBehalfOfTokenResult Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/types.md Demonstrates how to obtain and access data from an OnBehalfOfTokenResult. This is useful when exchanging an Auth0 access token for another targeting a downstream API. ```python result = await api_client.get_token_on_behalf_of( access_token=incoming_token, audience="https://calendar-api.example.com", scope="calendar:read" ) # result contains: # - access_token: str # - expires_in: int # - expires_at: int # - scope (optional): str # - token_type (optional): str # - issued_token_type (optional): str print(f"Token: {result['access_token']}") print(f"Expires in: {result['expires_in']} seconds") print(f"Expires at: {result['expires_at']} (Unix timestamp)") ``` -------------------------------- ### Python API Client Initialization and Token Verification Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/README.md Demonstrates how to initialize the ApiClient with options and verify an access token. This example shows real-world usage for obtaining a user's subject ID from claims. ```python from auth0_api_python import ApiClient, ApiClientOptions api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com" )) claims = await api_client.verify_access_token(token) print(claims["sub"]) # User ID ``` -------------------------------- ### Configure Custom Redis Cache Adapter Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/configuration.md Implement a custom cache adapter using Redis. This example defines a RedisCache class inheriting from CacheAdapter and configures the SDK to use it. ```python import redis from auth0_api_python import CacheAdapter class RedisCache(CacheAdapter): def __init__(self, redis_client): self.redis = redis_client def get(self, key): import json value = self.redis.get(key) return json.loads(value) if value else None def set(self, key, value, ttl_seconds=None): import json self.redis.set(key, json.dumps(value), ex=ttl_seconds) def delete(self, key): self.redis.delete(key) def clear(self): self.redis.flushdb() redis_client = redis.Redis(host='localhost', port=6379) options = ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com", cache_adapter=RedisCache(redis_client), cache_ttl_seconds=600 ) ``` -------------------------------- ### Get Token by Exchange Profile Example Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/types.md Use this snippet to exchange a subject token for an Auth0 access token. It demonstrates how to retrieve and access token details, including optional fields like scopes and ID tokens. ```python result = await api_client.get_token_by_exchange_profile( subject_token=token, subject_token_type="urn:example:subject-token", audience="https://api.example.com" ) print(f"Access token: {result['access_token']}") print(f"Expires in: {result['expires_in']} seconds") print(f"Expires at: {result['expires_at']}") # Optional fields if 'scope' in result: print(f"Scopes: {result['scope']}") if 'id_token' in result: print(f"ID token: {result['id_token']}") ``` -------------------------------- ### CacheAdapter Abstract Methods Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Defines the abstract methods that must be implemented by any custom cache adapter. These methods handle getting, setting, deleting, and clearing cache entries. ```APIDOC ## Abstract Methods All methods must be implemented in subclasses: ```python @abstractmethod def get(self, key: str) -> Optional[Any]: """Get value from cache by key.""" pass @abstractmethod def set(self, key: str, value: Any, ttl_seconds: Optional[int] = None) -> None: """Set value in cache with optional TTL.""" pass @abstractmethod def delete(self, key: str) -> None: """Delete value from cache.""" pass @abstractmethod def clear(self) -> None: """Clear all cache entries.""" pass ``` ``` -------------------------------- ### Flask Application with Auth0 Authentication Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/quickstart-examples.md This example demonstrates a complete Flask application that uses the Auth0 API Python SDK to verify access tokens for protected routes. Ensure your Auth0 domain and audience are correctly configured. ```python from flask import Flask, request, jsonify from auth0_api_python import ApiClient, ApiClientOptions, VerifyAccessTokenError import asyncio app = Flask(__name__) api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com" )) async def verify_request_async(headers): try: return await api_client.verify_request( headers={k.lower(): v for k, v in headers.items()}, http_method=request.method, http_url=request.url ) except VerifyAccessTokenError as e: raise Exception({"error": str(e), "status": 401}) @app.before_request def before_request(): if request.path == '/': return try: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) request.claims = loop.run_until_complete(verify_request_async(request.headers)) except Exception as e: error = e.args[0] if e.args else {"error": "unknown", "status": 500} return jsonify(error), error.get("status", 500) @app.route("/") def home(): return {"message": "Public endpoint"} @app.route("/protected") def protected(): return { "message": f"Hello {request.claims['sub']}", "scope": request.claims.get("scope", "") } if __name__ == "__main__": app.run(debug=True) ``` -------------------------------- ### Required Mode (DPoP Only) Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/quickstart-examples.md This example demonstrates how to enforce DPoP authentication exclusively. By setting `dpop_required=True`, the `ApiClient` will reject any requests that do not provide a valid DPoP token. ```APIDOC ## Required Mode (DPoP Only) ### Description Enforce DPoP authentication by setting `dpop_required=True` in `ApiClientOptions`. This configuration will cause the `verify_request` method to reject any authentication attempts that do not include a valid DPoP token. ### Usage ```python api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com", dpop_required=True # Only accept DPoP tokens )) async def handle_request(request): # This will accept DPoP claims = await api_client.verify_request( headers={ "authorization": "DPoP token", "dpop": "proof" }, http_method="POST", http_url="https://api.example.com/resource" ) # This will reject Bearer tokens try: claims = await api_client.verify_request( headers={"authorization": "Bearer token"} ) except InvalidAuthSchemeError: print("DPoP required, Bearer token rejected") ``` ``` -------------------------------- ### Handle ApiClient Configuration Errors Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/configuration.md Catch `ConfigurationError` and `MissingRequiredArgumentError` during ApiClient instantiation to gracefully handle invalid configuration settings. This example demonstrates handling a missing audience. ```python from auth0_api_python import ApiClient, ApiClientOptions, ConfigurationError try: api_client = ApiClient(ApiClientOptions( # Missing audience domain="my-tenant.us.auth0.com" )) except ConfigurationError as e: print(f"Configuration error: {e}") except MissingRequiredArgumentError as e: print(f"Missing argument: {e.argument}") ``` -------------------------------- ### Allowed Mode (Bearer or DPoP) Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/quickstart-examples.md This example shows how to configure the ApiClient to accept both Bearer tokens and DPoP tokens. The `dpop_required` option is set to `False`, allowing the `verify_request` method to handle either authentication scheme. ```APIDOC ## Allowed Mode (Bearer or DPoP) ### Description Configure the `ApiClient` to accept both Bearer and DPoP tokens by setting `dpop_required=False` in `ApiClientOptions`. The `verify_request` method can then process either type of authentication header. ### Usage ```python from auth0_api_python import ApiClient, ApiClientOptions api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com", dpop_enabled=True, dpop_required=False # Accept both Bearer and DPoP )) async def handle_request(request): # Will accept Bearer tokens claims1 = await api_client.verify_request( headers={"authorization": "Bearer eyJ..."} ) # Will also accept DPoP tokens claims2 = await api_client.verify_request( headers={ "authorization": "DPoP eyJ...", "dpop": "eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2In0..." }, http_method="GET", http_url="https://api.example.com/resource" ) ``` ``` -------------------------------- ### Handle ConfigurationError in Auth0 API Client Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/errors.md Catch ConfigurationError when initializing ApiClient if required options like audience or domain are missing. This ensures graceful handling of setup issues. ```python from auth0_api_python import ConfigurationError try: api_client = ApiClient(ApiClientOptions( audience="https://api.example.com" # domain not provided )) except ConfigurationError as e: print(f"Configuration error: {e}") # "Must provide either 'domain' or 'domains' parameter" ``` -------------------------------- ### Initialize ApiClient Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/quickstart-examples.md Demonstrates how to initialize the ApiClient with options and use it to verify an access token. ```python from auth0_api_python import ApiClient, ApiClientOptions import asyncio async def main(): # Create client api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com" )) # Use client claims = await api_client.verify_access_token("token-string") print(f"User: {claims['sub']}") asyncio.run(main()) ``` -------------------------------- ### Initialize ApiClient Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Instantiate the ApiClient with domain and audience configuration. Ensure audience is provided and domain configuration is valid. ```python from auth0_api_python import ApiClient, ApiClientOptions api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com" )) ``` -------------------------------- ### Initialize ApiClientOptions Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Configure the ApiClient with domain, audience, and various security and caching settings. Ensure either 'domain' or 'domains' is provided for multi-domain support. ```python options = ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com", client_id="client_id_value", client_secret="client_secret_value", dpop_enabled=True, dpop_required=False, cache_ttl_seconds=600, timeout=10.0 ) ``` -------------------------------- ### get Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Retrieves a value from the cache by its key. This operation also updates the access order for LRU tracking. ```APIDOC ## get ### Description Retrieves a value from the cache by its key. This operation also updates the access order for LRU tracking. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Method Signature `get(self, key: str) -> Optional[Any]` ### Parameters - **key** (str) - Required - Cache key ### Returns - **Optional[Any]** - Cached value if found and not expired, `None` otherwise. ``` -------------------------------- ### Get Current Actor Function Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/module-exports.md Retrieves the current actor from a token delegation chain. Requires claims as input. ```python from auth0_api_python import get_current_actor from typing import Mapping, Any, Optional def get_current_actor(claims: Mapping[str, Any]) -> Optional[str]: # Implementation details... ``` -------------------------------- ### Getting Current Actor and Delegation Chain Source: https://github.com/auth0/auth0-api-python/blob/main/README.md Helper functions to extract the current actor and the full delegation chain from verified token claims. ```APIDOC ## get_current_actor() and get_delegation_chain() ### Description These utility functions help parse the `act` claim from verified token claims to determine the current actor for authorization and to retrieve the full delegation chain for logging or auditing. ### Method `get_current_actor(claims: dict) -> str` `get_delegation_chain(claims: dict) -> list[dict]` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python claims = await api_client.verify_access_token(access_token=access_token) current_actor = get_current_actor(claims) delegation_chain = get_delegation_chain(claims) ``` ### Response #### Success Response (200) - **get_current_actor**: Returns a string representing the `sub` of the outermost `act` claim. - **get_delegation_chain**: Returns a list of dictionaries, where each dictionary represents an actor in the delegation chain, ordered from the initial caller to the current one. #### Response Example ```python # For get_current_actor "mcp_server_client_id" # For get_delegation_chain [ { "aud": "https://downstream.example.com", "sub": "client_id_of_intermediary" }, { "aud": "https://intermediary.example.com", "sub": "client_id_of_original_caller" } ] ``` ``` -------------------------------- ### Recommended Core Imports Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/module-exports.md Import the main `ApiClient` and its options for interacting with the Auth0 API. ```python # Core functionality from auth0_api_python import ApiClient, ApiClientOptions ``` -------------------------------- ### Get Delegation Chain Function Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/module-exports.md Extracts the full delegation chain from a token's claims. Returns a list of strings representing the chain. ```python from auth0_api_python import get_delegation_chain from typing import Mapping, Any def get_delegation_chain(claims: Mapping[str, Any]) -> list[str]: # Implementation details... ``` -------------------------------- ### InMemoryCache Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/module-exports.md The default in-memory cache implementation. It supports LRU eviction and provides methods for getting, setting, deleting, and clearing cached values. ```APIDOC ## InMemoryCache Default in-memory cache implementation with LRU eviction. **Import:** `from auth0_api_python import InMemoryCache` **Methods:** - `get(key)` - Retrieve cached value - `set(key, value, ttl_seconds)` - Store value with TTL - `delete(key)` - Remove cached value - `clear()` - Clear all entries ``` -------------------------------- ### ApiClient Constructor Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Initializes a new ApiClient instance with the provided configuration. It validates essential settings like domain and audience. ```APIDOC ## ApiClient Constructor ### Description Creates a new ApiClient instance with the given configuration. Validates that audience is provided and domain/domains configuration is valid. ### Signature ```python ApiClient(options: ApiClientOptions) ``` ### Parameters #### Path Parameters - **options** (ApiClientOptions) - Required - Configuration object containing domain, audience, and optional settings ### Raises - `MissingRequiredArgumentError`: If audience is not provided - `ConfigurationError`: If domain/domains configuration is invalid, or if cache settings are invalid ### Example ```python from auth0_api_python import ApiClient, ApiClientOptions api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com" )) ``` ``` -------------------------------- ### ApiClientOptions Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/module-exports.md Configuration object for ApiClient initialization. ```APIDOC ## ApiClientOptions ### Description Configuration object for ApiClient initialization. ### Import `from auth0_api_python import ApiClientOptions` ### Properties - `domain` - Single Auth0 domain - `domains` - Multiple domains (list or resolver) - `audience` - Expected audience claim - `custom_fetch` - Custom HTTP fetch function - `cache_adapter` - Custom cache implementation - `cache_ttl_seconds` - Cache TTL in seconds - `cache_max_entries` - Max cache entries - `dpop_enabled` - Enable DPoP support - `dpop_required` - Require DPoP authentication - `dpop_iat_leeway` - DPoP iat leeway in seconds - `dpop_iat_offset` - DPoP iat max age in seconds - `client_id` - OAuth client ID - `client_secret` - OAuth client secret - `timeout` - HTTP timeout in seconds ``` -------------------------------- ### Get Access Token for Connection Source: https://github.com/auth0/auth0-api-python/blob/main/README.md Obtain an access token for an upstream identity provider via a connection using `get_access_token_for_connection`. Requires client ID and secret. ```python import asyncio from auth0_api_python import ApiClient, ApiClientOptions async def main(): api_client = ApiClient(ApiClientOptions( domain="", audience="", client_id="", client_secret="", )) connection = "my-connection" # The Auth0 connection to the upstream idp access_token = "..." # The Auth0 access token to exchange connection_access_token = await api_client.get_access_token_for_connection({"connection": connection, "access_token": access_token}) # The returned token is the access token for the upstream idp print(connection_access_token) asyncio.run(main()) ``` -------------------------------- ### Asynchronous Domains Resolver Function Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/types.md An example of an asynchronous function that implements the DomainsResolver type. This resolver fetches allowed domains from a database based on the token's issuer. ```python async def my_async_resolver(context: DomainsResolverContext) -> list[str]: # Lookup domains from database based on issuer issuer = context['unverified_iss'] domains = await db.lookup_domains_for_issuer(issuer) return domains ``` -------------------------------- ### Configure ApiClient with DPoP Enabled and Allowed Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/configuration.md Enable DPoP support, allowing both Bearer and DPoP tokens. Configure leeway and offset for IAT validation. The `domain` and `audience` are required. ```python options = ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com", dpop_enabled=True, dpop_required=False, dpop_iat_leeway=30, dpop_iat_offset=300 # 5 minutes ) ``` -------------------------------- ### Configuration Error: Empty Domains List Source: https://github.com/auth0/auth0-api-python/blob/main/docs/MultipleCustomDomain.md Catch ConfigurationError during ApiClient initialization if the 'domains' list is empty. ```python # Empty domains list try: api_client = ApiClient(ApiClientOptions(domains=[], audience="https://api.example.com")) except ConfigurationError as e: print(e) # "domains list cannot be empty" ``` -------------------------------- ### Synchronous Domains Resolver Function Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/types.md An example of a synchronous function that implements the DomainsResolver type. This resolver determines allowed domains based on the request's host header. ```python from auth0_api_python import DomainsResolverContext def my_resolver(context: DomainsResolverContext) -> list[str]: # Simple resolver based on request host host = (context.get('request_headers') or {}).get('host') if host == 'api.brand.com': return ['brand.auth0.com'] return ['tenant.auth0.com'] ``` -------------------------------- ### ApiClientOptions Constructor Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Configuration class for ApiClient initialization. Allows setting various parameters for domain, audience, caching, DPoP, and client credentials. ```APIDOC ## ApiClientOptions ### Description Configuration class for ApiClient initialization. ### Constructor `ApiClientOptions( domain: Optional[str] = None, audience: str = "", domains: Optional[Union[list[str], Callable[[dict], list[str]]]] = None, custom_fetch: Optional[Callable[..., object]] = None, cache_adapter: Optional[CacheAdapter] = None, cache_ttl_seconds: int = 600, cache_max_entries: int = 100, dpop_enabled: bool = True, dpop_required: bool = False, dpop_iat_leeway: int = 30, dpop_iat_offset: int = 300, client_id: Optional[str] = None, client_secret: Optional[str] = None, timeout: float = 10.0 )` ### Parameters #### Constructor Parameters - **domain** (str) - Optional - None - Auth0 domain (e.g., "tenant.us.auth0.com"). Required if domains not provided - **audience** (str) - Required - "" - Expected 'aud' claim in access tokens - **domains** (list[str] or callable) - Optional - None - List of allowed domains or resolver function for multi-domain mode. Required if domain not provided - **custom_fetch** (callable) - Optional - None - Custom HTTP fetch function to override httpx - **cache_adapter** (CacheAdapter) - Optional - None - Custom cache implementation (defaults to InMemoryCache) - **cache_ttl_seconds** (int) - Optional - 600 - Time-to-live for cache entries in seconds - **cache_max_entries** (int) - Optional - 100 - Maximum number of cached entries before LRU eviction - **dpop_enabled** (bool) - Optional - True - Enable DPoP support (allows Bearer and DPoP) - **dpop_required** (bool) - Optional - False - Require DPoP authentication (rejects Bearer tokens) - **dpop_iat_leeway** (int) - Optional - 30 - Clock skew tolerance for DPoP proof iat claim (seconds) - **dpop_iat_offset** (int) - Optional - 300 - Maximum age of DPoP proof iat claim (seconds) - **client_id** (str) - Optional - None - Client ID for token exchange flows - **client_secret** (str) - Optional - None - Client secret for token exchange flows - **timeout** (float) - Optional - 10.0 - HTTP timeout for token endpoint requests (seconds) *Note: Either `domain` or `domains` must be provided, but not both simultaneously in single-domain mode. ### Example ```python options = ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com", client_id="client_id_value", client_secret="client_secret_value", dpop_enabled=True, dpop_required=False, cache_ttl_seconds=600, timeout=10.0 ) ``` ``` -------------------------------- ### Build Package Source: https://github.com/auth0/auth0-api-python/blob/main/CONTRIBUTING.md Compiles the Python package for distribution. ```bash poetry build ``` -------------------------------- ### Initialize InMemoryCache Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Instantiate the cache with a specified maximum number of entries. Defaults to 100 if not provided. ```python from auth0_api_python import InMemoryCache cache = InMemoryCache(max_entries=50) ``` -------------------------------- ### Get Cache Entry Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Retrieve a value from the cache by its key. Returns None if the key is not found or the entry has expired. Accessing an item updates its position in the LRU tracking. ```python value = cache.get(key='my_key') ``` -------------------------------- ### Get Current Actor from Claims Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/utility-functions.md Retrieves the subject identifier of the current actor from the outermost 'act.sub' claim. Use this for authorization decisions. If no delegation is present, it returns None. ```python from auth0_api_python import get_current_actor claims = await api_client.verify_access_token(token) current_actor = get_current_actor(claims) if current_actor: print(f"Authorized actor: {current_actor}") # Use for authorization decisions else: print(f"Direct user: {claims['sub']}") # No delegation, use subject claim ``` -------------------------------- ### Configure ApiClient with Static List of Domains Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/configuration.md Provide a list of allowed domains using the `domains` parameter for multi-domain support. The `audience` is also required. ```python options = ApiClientOptions( domains=[ "my-tenant.us.auth0.com", "legacy-tenant.auth0.com", "custom.example.com" ], audience="https://api.example.com" ) ``` -------------------------------- ### Configure DPoP Options Source: https://github.com/auth0/auth0-api-python/blob/main/README.md Customize DPoP behavior by setting options such as enabling/disabling support, enforcing DPoP, and configuring clock skew tolerance and maximum proof age. ```python api_client = ApiClient(ApiClientOptions( domain="", audience="", dpop_enabled=True, # Enable/disable DPoP support dpop_required=False, # Require DPoP (reject Bearer) dpop_iat_leeway=30, # Clock skew tolerance (seconds) dpop_iat_offset=300, # Maximum proof age (seconds) )) ``` -------------------------------- ### Configure ApiClient with Environment Variables Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/configuration.md Pass configuration options to ApiClientOptions by reading values from environment variables. Ensure all necessary environment variables are set before running. ```python import os from auth0_api_python import ApiClient, ApiClientOptions options = ApiClientOptions( domain=os.getenv("AUTH0_DOMAIN"), audience=os.getenv("AUTH0_AUDIENCE"), client_id=os.getenv("AUTH0_CLIENT_ID"), client_secret=os.getenv("AUTH0_CLIENT_SECRET"), dpop_enabled=os.getenv("AUTH0_DPOP_ENABLED", "true").lower() == "true", dpop_required=os.getenv("AUTH0_DPOP_REQUIRED", "false").lower() == "true", cache_ttl_seconds=int(os.getenv("AUTH0_CACHE_TTL", "600")), timeout=float(os.getenv("AUTH0_TIMEOUT", "10.0")) ) api_client = ApiClient(options) ``` -------------------------------- ### Get Unverified JWT Header Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/utility-functions.md Parses the header of a JWT without verifying its signature. Useful for inspecting token metadata before validation. Requires the JWT token as a string or bytes. ```python from auth0_api_python.utils import get_unverified_header header = get_unverified_header(token) print(header["alg"]) print(header["kid"]) print(header["typ"]) ``` -------------------------------- ### Configure Multi-Custom Domain Support (Dynamic Resolver) Source: https://github.com/auth0/auth0-api-python/blob/main/README.md Implement a dynamic domain resolver function that determines allowed domains at runtime based on request context. This provides flexibility for complex routing or migration scenarios. ```python from auth0_api_python import ApiClient, ApiClientOptions, DomainsResolverContext def resolve_domains(context: DomainsResolverContext) -> list[str]: # Determine allowed domains based on the request return ["tenant.auth0.com", "auth.example.com"] api_client = ApiClient(ApiClientOptions( domains=resolve_domains, audience="https://api.example.com" )) ``` -------------------------------- ### Get Unverified JWT Payload Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/utility-functions.md Parses the payload (claims) of a JWT without verifying its signature. Useful for inspecting token claims before validation. Requires the JWT token as a string or bytes. ```python from auth0_api_python.utils import get_unverified_payload claims = get_unverified_payload(token) print(claims["iss"]) print(claims["aud"]) print(claims["sub"]) print(claims["iat"]) print(claims["exp"]) ``` -------------------------------- ### Run Unit Tests Source: https://github.com/auth0/auth0-api-python/blob/main/CONTRIBUTING.md Executes unit tests using Poetry. This command should be run before submitting a Pull Request. ```bash poetry run pytest ``` -------------------------------- ### Verify Access Token with Auth0 API Python Source: https://github.com/auth0/auth0-api-python/blob/main/EXAMPLES.md Use `verify_access_token` to manually extract and validate a Bearer token from request headers. Ensure the 'authorization' header is present and starts with 'Bearer '. ```python import asyncio from auth0_api_python import ApiClient, ApiClientOptions async def validate_bearer_token(headers): api_client = ApiClient(ApiClientOptions( domain="your-tenant.auth0.com", audience="https://api.example.com" )) try: # Extract the token from the Authorization header auth_header = headers.get("authorization", "") if not auth_header.startswith("Bearer "): return {"error": "Missing or invalid authorization header"}, 401 token = auth_header.split(" ")[1] # Verify the access token claims = await api_client.verify_access_token(token) return {"success": True, "user": claims["sub"]} except Exception as e: return {"error": str(e)}, getattr(e, "get_status_code", lambda: 401)() # Example usage headers = {"authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."} result = asyncio.run(validate_bearer_token(headers)) ``` -------------------------------- ### Hybrid Mode: Domain and Domains Configuration Source: https://github.com/auth0/auth0-api-python/blob/main/docs/MultipleCustomDomain.md Configure both a single 'domain' for client-initiated flows and a 'domains' list for token verification. This is useful for migration scenarios. ```python api_client = ApiClient(ApiClientOptions( domain="tenant.auth0.com", # Used for token exchange discovery domains=[ "tenant.auth0.com", "auth.newdomain.com" ], # Used for token verification audience="https://api.example.com", client_id="", client_secret="" )) # Token verification uses the domains list claims = await api_client.verify_access_token(access_token) # Token exchange uses the domain parameter for discovery result = await api_client.get_token_by_exchange_profile( subject_token=access_token, subject_token_type="urn:example:subject-token" ) ``` -------------------------------- ### Simple Token Verification Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/quickstart-examples.md Shows how to perform a simple verification of an access token and handle potential errors. ```python from auth0_api_python import ApiClient, ApiClientOptions, VerifyAccessTokenError api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com" )) async def verify_token(token: str): try: claims = await api_client.verify_access_token(token) return { "valid": True, "user": claims["sub"], "scope": claims.get("scope", "") } except VerifyAccessTokenError as e: return { "valid": False, "error": str(e) } ``` -------------------------------- ### Get Access Token for Connection Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Exchanges an Auth0 access token for an access token for an upstream identity provider connection using Token Vault. Requires client credentials to be configured in ApiClientOptions. ```python async def get_access_token_for_connection(options: dict[str, Any]) -> dict[str, Any] ``` ```python result = await api_client.get_access_token_for_connection({ "connection": "my-connection", "access_token": incoming_token }) print(f"Connection token: {result['access_token']}") ``` -------------------------------- ### Create Auth0 API Client Source: https://github.com/auth0/auth0-api-python/blob/main/README.md Instantiate the ApiClient with your Auth0 domain and audience. These values can be found in your Auth0 Dashboard. ```python from auth0_api_python import ApiClient, ApiClientOptions api_client = ApiClient(ApiClientOptions( domain="", audience="" )) ``` -------------------------------- ### Get Access Token for Connection Source: https://github.com/auth0/auth0-api-python/blob/main/README.md Obtain an access token for an upstream identity provider (IdP) via a specified connection. This is useful for exchanging an Auth0 access token for an upstream IdP token. ```APIDOC ## Get Access Token for Connection ### Description Retrieves an access token for an upstream identity provider (IdP) using a specified Auth0 connection. This method facilitates the exchange of an Auth0 access token for an upstream IdP token. ### Method `get_access_token_for_connection` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **connection** (string) - Required - The Auth0 connection to the upstream IdP. - **access_token** (string) - Required - The Auth0 access token to exchange. ### Request Example ```python import asyncio from auth0_api_python import ApiClient, ApiClientOptions async def main(): api_client = ApiClient(ApiClientOptions( domain="", audience="", client_id="", client_secret="", )) connection = "my-connection" # The Auth0 connection to the upstream idp access_token = "..." # The Auth0 access token to exchange connection_access_token = await api_client.get_access_token_for_connection({"connection": connection, "access_token": access_token}) # The returned token is the access token for the upstream idp print(connection_access_token) asyncio.run(main()) ``` ### Response #### Success Response (200) - **upstream_access_token** (string) - The access token for the upstream IdP. #### Response Example ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` ``` -------------------------------- ### Verify and Inspect Delegated Access Token Source: https://github.com/auth0/auth0-api-python/blob/main/EXAMPLES.md Use this snippet to verify an access token and extract the current actor and delegation chain. Ensure the `auth0_api_python` library is installed and configured with your Auth0 domain and audience. ```python import asyncio import logging from auth0_api_python import ( ApiClient, ApiClientOptions, get_current_actor, get_delegation_chain, ) logger = logging.getLogger(__name__) async def inspect_delegated_token(): api_client = ApiClient(ApiClientOptions( domain="your-tenant.auth0.com", audience="https://calendar-api.example.com" )) access_token = "delegated-auth0-access-token" claims = await api_client.verify_access_token(access_token=access_token) current_actor = get_current_actor(claims) delegation_chain = get_delegation_chain(claims) if current_actor != "mcp_server_client_id": raise PermissionError("unexpected actor") logger.info( "delegated request", extra={ "user_sub": claims["sub"], "current_actor": current_actor, "delegation_chain": delegation_chain, }, ) return { "user_sub": claims["sub"], "current_actor": current_actor, "delegation_chain": delegation_chain, } asyncio.run(inspect_delegated_token()) ``` -------------------------------- ### Configuration Error: Missing Domain Source: https://github.com/auth0/auth0-api-python/blob/main/docs/MultipleCustomDomain.md Catch ConfigurationError during ApiClient initialization if neither 'domain' nor 'domains' parameters are provided. ```python from auth0_api_python import ApiClient, ApiClientOptions, ConfigurationError # Neither domain nor domains provided try: api_client = ApiClient(ApiClientOptions(audience="https://api.example.com")) except ConfigurationError as e: print(e) # "Must provide either 'domain' or 'domains' parameter..." ``` -------------------------------- ### Async Domain Resolver Function Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/quickstart-examples.md Implement an asynchronous function to dynamically resolve allowed Auth0 domains, for example, by querying a database based on the token's issuer. This is suitable for scenarios requiring external lookups. ```python async def resolve_domains_from_db(context): """Lookup allowed domains from database based on issuer""" issuer = context['unverified_iss'] # Query database for domains allowed for this issuer domains = await db.get_allowed_domains(issuer) return domains api_client = ApiClient(ApiClientOptions( domains=resolve_domains_from_db, audience="https://api.example.com" )) ``` -------------------------------- ### Configure ApiClient with Static List of Domains Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/configuration.md Use a static list of domains in ApiClientOptions to support multiple Auth0 tenants or custom domains. Tokens from any listed domain will be accepted. ```python options = ApiClientOptions( domains=[ "tenant.auth0.com", "custom1.example.com", "custom2.example.com" ], audience="https://api.example.com" ) api_client = ApiClient(options) # Any token issued by any of the three domains is accepted claims = await api_client.verify_access_token(token) ``` -------------------------------- ### ApiClient Configuration with Domains Resolver Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/types.md Shows how to configure the ApiClient with a custom domains resolver, either synchronous or asynchronous, to manage allowed domains for API requests. ```python api_client = ApiClient(ApiClientOptions( domains=my_resolver, # or my_async_resolver audience="https://api.example.com" )) ``` -------------------------------- ### Get Delegation Chain from Claims Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/utility-functions.md Returns the full delegation chain as a list of actor subject identifiers, ordered from newest to oldest. This is useful for auditing and attribution, not direct authorization. An empty list is returned if there is no delegation. ```python from auth0_api_python import get_delegation_chain import logging logger = logging.getLogger(__name__) claims = await api_client.verify_access_token(token) # Get the full delegation chain chain = get_delegation_chain(claims) if chain: # Token was issued through delegation logger.info( "delegated_request", extra={ "user_sub": claims["sub"], "delegation_chain": chain, "current_actor": chain[0] if chain else None, "prior_actors": chain[1:] if len(chain) > 1 else [] } ) else: # Direct token, no delegation logger.info( "direct_request", extra={"user_sub": claims["sub"]} ) ``` -------------------------------- ### Static Domain List Configuration Source: https://github.com/auth0/auth0-api-python/blob/main/docs/MultipleCustomDomain.md Configure the API client to accept tokens from a predefined list of domains. The SDK validates the token's issuer against this list. ```python from auth0_api_python import ApiClient, ApiClientOptions api_client = ApiClient(ApiClientOptions( domains=[ "tenant.auth0.com", "auth.example.com", "auth.acme.org" ], audience="https://api.example.com" )) # Tokens from any of the three domains are accepted claims = await api_client.verify_access_token(access_token) ``` -------------------------------- ### Recommended Delegation Chain Imports Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/module-exports.md Import functions for analyzing delegation chains, useful for understanding token origins and security contexts. ```python # Delegation chain analysis from auth0_api_python import get_current_actor, get_delegation_chain ``` -------------------------------- ### Get Delegation Chain and Current Actor Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/types.md Demonstrates how to extract the current actor's subject and the full delegation chain from JWT claims using helper functions. Ensure the access token is verified before processing claims. ```python from auth0_api_python import get_current_actor, get_delegation_chain claims = await api_client.verify_access_token(access_token) # Get the current actor (outermost act.sub) current_actor = get_current_actor(claims) print(f"Current actor: {current_actor}") # Get the full delegation chain chain = get_delegation_chain(claims) print(f"Delegation chain: {chain}") # Output: ['mcp_server_id', 'user_service_id', 'original_user_id'] ``` -------------------------------- ### Enable DPoP Authentication (Allowed Mode) Source: https://github.com/auth0/auth0-api-python/blob/main/README.md Configure the ApiClient to accept both Bearer and DPoP tokens. This mode is suitable for gradual migration to DPoP. The `verify_request` method automatically detects and handles the token scheme. ```python api_client = ApiClient(ApiClientOptions( domain="", audience="", dpop_enabled=True, # Default - enables DPoP support dpop_required=False # Default - allows both Bearer and DPoP )) # Use verify_request() for automatic scheme detection result = await api_client.verify_request( headers={ "authorization": "DPoP eyJ0eXAiOiJKV1Q...", # DPoP scheme "dpop": "eyJ0eXAiOiJkcG9wK2p3dC...", # DPoP proof }, http_method="GET", http_url="https://api.example.com/resource" ) ``` -------------------------------- ### Migrate to Multiple Custom Domains Source: https://github.com/auth0/auth0-api-python/blob/main/docs/MultipleCustomDomain.md Gradually migrate from a single Auth0 domain to multiple custom domains. Phase 1 uses the current single domain. Phase 2 adds a new domain, accepting tokens from both. Phase 3 fully enables MCD with all specified domains. ```python # Phase 1: Start with single domain (current state) client = ApiClient(ApiClientOptions( domain="tenant.auth0.com", audience="https://api.example.com" )) ``` ```python # Phase 2: Add new domain alongside existing (during migration) # Tokens from both domains are now accepted client = ApiClient(ApiClientOptions( domain="tenant.auth0.com", domains=["tenant.auth0.com", "auth.newdomain.com"], audience="https://api.example.com" )) ``` ```python # Phase 3: Full MCD with all domains (after migration) client = ApiClient(ApiClientOptions( domain="tenant.auth0.com", domains=["tenant.auth0.com", "auth.newdomain.com", "auth.other.com"], audience="https://api.example.com" )) ``` -------------------------------- ### Access Standard JWT Claims Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/types.md Shows how to access common JWT claims like 'sub', 'iss', 'aud', 'scope', and 'exp' from a verified access token. Includes examples for custom claims and the optional 'act' claim for delegation. ```python claims = await api_client.verify_access_token(access_token) print(f"User: {claims['sub']}") print(f"Issuer: {claims['iss']}") print(f"Audience: {claims['aud']}") print(f"Scopes: {claims.get('scope', '')}") print(f"Expires: {claims['exp']}") # Custom claims if 'custom_claim' in claims: print(f"Custom claim: {claims['custom_claim']}") # Delegation chain (if present) if 'act' in claims: current_actor = get_current_actor(claims) print(f"Current actor: {current_actor}") ``` -------------------------------- ### Complete ApiClient Configuration Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/configuration.md Configure the ApiClient with all available options for single-domain mode, including domain, audience, DPoP settings, cache parameters, client credentials, and HTTP timeout. ```python from auth0_api_python import ApiClient, ApiClientOptions # Single-domain mode with all options options = ApiClientOptions( # Domain configuration domain="my-tenant.us.auth0.com", # Token verification audience="https://api.example.com", # DPoP configuration dpop_enabled=True, dpop_required=False, dpop_iat_leeway=30, dpop_iat_offset=300, # Cache configuration cache_ttl_seconds=600, cache_max_entries=100, # Client credentials for token exchange client_id="your_client_id", client_secret="your_client_secret", # HTTP request configuration timeout=10.0 ) api_client = ApiClient(options) ``` -------------------------------- ### Exchange Subject Token for Auth0 Tokens Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/api-reference.md Use this method to exchange a subject token for Auth0 tokens via RFC 8693 custom token exchange. Requires client credentials to be configured in ApiClientOptions. Ensure subject_token is not blank and does not start with 'Bearer '. ```python async def get_token_by_exchange_profile( subject_token: str, subject_token_type: str, audience: Optional[str] = None, scope: Optional[str] = None, requested_token_type: Optional[str] = None, extra: Optional[Mapping[str, Union[str, Sequence[str]]]] = None ) -> dict[str, Any]: ... result = await api_client.get_token_by_exchange_profile( subject_token=legacy_token, subject_token_type="urn:example:legacy-token", audience="https://api.example.com", scope="openid profile email", extra={"device_id": "device-123", "session_id": "sess-456"} ) print(f"Access token: {result['access_token']}") ``` -------------------------------- ### Handle VerifyAccessTokenError in Python Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/errors.md Demonstrates how to catch and process a VerifyAccessTokenError when an access token verification fails. It shows how to retrieve status code, error code, description, and headers from the exception. ```python from auth0_api_python import ApiClient, ApiClientOptions, VerifyAccessTokenError api_client = ApiClient(ApiClientOptions( domain="my-tenant.us.auth0.com", audience="https://api.example.com" )) try: claims = await api_client.verify_access_token(access_token) except VerifyAccessTokenError as e: status_code = e.get_status_code() # 401 error_code = e.get_error_code() # "invalid_token" description = e.get_error_description() headers = e.get_headers() # Return HTTP 401 with WWW-Authenticate header return { "status": status_code, "error": error_code, "error_description": description, "headers": headers } ``` -------------------------------- ### Static Domain List Configuration Source: https://github.com/auth0/auth0-api-python/blob/main/_autodocs/quickstart-examples.md Configure the ApiClient with a static list of domains to support multiple Auth0 tenants or custom domains. This is useful when the allowed domains are known beforehand. ```python from auth0_api_python import ApiClient, ApiClientOptions api_client = ApiClient(ApiClientOptions( domains=[ "tenant1.auth0.com", "tenant2.auth0.com", "custom.example.com" ], audience="https://api.example.com" )) async def verify(): # Accepts tokens from any of the three domains claims = await api_client.verify_access_token(token) ```