The auth0-api-python library allows you to secure APIs running on Python, particularly for verifying Auth0-issued access tokens.
It’s intended as a foundation for building more framework-specific integrations (e.g., with FastAPI, Django, etc.), but you can also use it directly in any Python server-side environment.
📚 Documentation - 🚀 Getting Started - 💬 Feedback
This SDK provides comprehensive support for securing APIs with Auth0-issued access tokens:
- Bearer Token Authentication - Traditional OAuth 2.0 Bearer tokens (RS256)
- DPoP Authentication - Enhanced security with Demonstrating Proof-of-Possession (ES256)
- Mixed Mode Support - Seamlessly handles both Bearer and DPoP in the same API
- Unified Entry Point:
verify_request()- automatically detects and validates Bearer or DPoP schemes - OIDC Discovery - Automatic fetching of Auth0 metadata and JWKS
- JWT Validation - Complete RS256 signature verification with claim validation
- DPoP Proof Verification - Full RFC 9449 compliance with ES256 signature validation
- Flexible Configuration - Support for both "Allowed" and "Required" DPoP modes
- Comprehensive Error Handling - Detailed errors with proper HTTP status codes and WWW-Authenticate headers
- Framework Agnostic - Works with FastAPI, Django, Flask, or any Python web framework
- Docs Site - explore our docs site and learn more about Auth0.
This library is part of Auth0's Python ecosystem for server-side authentication and API security. Related SDKs:
- auth0-auth-js - JavaScript/TypeScript monorepo containing:
@auth0/auth0-auth-js- Core authentication client (low-level primitives)@auth0/auth0-api-js- Server-side API security (Node.js equivalent of this library)@auth0/auth0-server-js- Server-side web app authentication (session management)
This library requires Python 3.9+.
pip install auth0-api-pythonIf you’re using Poetry:
poetry install auth0-api-pythonCreate an instance of the ApiClient. This instance will be imported and used anywhere we need access to the methods.
from auth0_api_python import ApiClient, ApiClientOptions
api_client = ApiClient(ApiClientOptions(
domain="<AUTH0_DOMAIN>",
audience="<AUTH0_AUDIENCE>"
))- The
AUTH0_DOMAINcan be obtained from the Auth0 Dashboard once you've created an application. - The
AUTH0_AUDIENCEis the identifier of the API. You can find this in the APIs section of the Auth0 Dashboard.
Use the verify_access_token method to validate access tokens. The method automatically checks critical claims like iss, aud, exp, nbf.
import asyncio
from auth0_api_python import ApiClient, ApiClientOptions
async def main():
api_client = ApiClient(ApiClientOptions(
domain="<AUTH0_DOMAIN>",
audience="<AUTH0_AUDIENCE>"
))
access_token = "..."
decoded_and_verified_token = await api_client.verify_access_token(access_token=access_token)
print(decoded_and_verified_token)
asyncio.run(main())In this example, the returned dictionary contains the decoded claims (like sub, scope, etc.) from the verified token.
If you need to get an access token for an upstream idp via a connection, you can use the get_access_token_for_connection method:
import asyncio
from auth0_api_python import ApiClient, ApiClientOptions
async def main():
api_client = ApiClient(ApiClientOptions(
domain="<AUTH0_DOMAIN>",
audience="<AUTH0_AUDIENCE>",
client_id="<AUTH0_CLIENT_ID>",
client_secret="<AUTH0_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())More info https://auth0.com/docs/secure/tokens/token-vault
Note
This feature is currently available in Early Access for Enterprise customers. Please reach out to Auth0 support to get it enabled for your tenant.
This feature requires a confidential client (both client_id and client_secret must be configured).
Custom Token Exchange allows you to exchange a subject token for Auth0 tokens using RFC 8693. This is useful for:
- Getting Auth0 tokens for another audience
- Integrating external identity providers
- Migrating to Auth0
import asyncio
from auth0_api_python import ApiClient, ApiClientOptions
async def main():
api_client = ApiClient(ApiClientOptions(
domain="<AUTH0_DOMAIN>",
audience="<AUTH0_AUDIENCE>",
client_id="<AUTH0_CLIENT_ID>",
client_secret="<AUTH0_CLIENT_SECRET>",
timeout=10.0 # Optional: HTTP timeout in seconds (default: 10.0)
))
subject_token = "..." # Token from your legacy system or external source
result = await api_client.get_token_by_exchange_profile(
subject_token=subject_token,
subject_token_type="urn:example:subject-token",
audience="https://api.example.com", # Optional - omit if your Action or tenant configuration sets the audience
scope="openid profile email", # Optional
requested_token_type="urn:ietf:params:oauth:token-type:access_token" # Optional
)
# Result contains access_token, expires_in, expires_at
# id_token, refresh_token, and scope are profile/Action dependent (not guaranteed; scope may be empty)
asyncio.run(main())Important:
- Client authentication is sent via HTTP Basic (
client_id/client_secret), not in the form body. - Do not prefix
subject_tokenwith "Bearer " - send the raw token value only (checked case-insensitively). - The
subject_token_typemust match a Token Exchange Profile configured in Auth0. This URI identifies which profile will process the exchange and must not use reserved OAuth namespaces (IETF or vendor-controlled). Use your own collision-resistant namespace. See the Custom Token Exchange documentation for naming guidance. - If neither an explicit
audiencenor tenant/Action logic sets it, you may receive a token not targeted at your API.
You can pass additional parameters for your Token Exchange Profile or Actions via the extra parameter. These are sent as form fields to Auth0 and may be inspected by Actions:
result = await api_client.get_token_by_exchange_profile(
subject_token=subject_token,
subject_token_type="urn:example:subject-token",
audience="https://api.example.com",
extra={
"device_id": "device-12345",
"session_id": "sess-abc"
}
)Warning
Extra parameters are sent as form fields and may appear in logs. Do not include secrets or sensitive data. Reserved OAuth parameter names (like grant_type, client_id, scope) cannot be used and will raise an error. Arrays are supported but limited to 20 values per key to prevent abuse.
from auth0_api_python import GetTokenByExchangeProfileError, ApiError
try:
result = await api_client.get_token_by_exchange_profile(
subject_token=subject_token,
subject_token_type="urn:example:subject-token"
)
except GetTokenByExchangeProfileError as e:
# Validation errors (invalid token format, missing credentials, reserved params, etc.)
print(f"Validation error: {e}")
except ApiError as e:
# Token endpoint errors (invalid_grant, network issues, malformed responses, etc.)
print(f"API error: {e.code} - {e.message} (status: {e.status_code})")Related SDKs: auth0-auth-js (see @auth0/auth0-api-js package for Node.js equivalent)
More info: https://auth0.com/docs/authenticate/custom-token-exchange
If your application demands extra claims, specify them with required_claims:
decoded_and_verified_token = await api_client.verify_access_token(
access_token=access_token,
required_claims=["my_custom_claim"]
)If the token lacks my_custom_claim or fails any standard check (issuer mismatch, expired token, invalid signature), the method raises a VerifyAccessTokenError.
Note
This feature is currently available in Early Access. Please reach out to Auth0 support to get it enabled for your tenant.
This library supports DPoP (Demonstrating Proof-of-Possession) for enhanced security, allowing clients to prove possession of private keys bound to access tokens.
Accepts both Bearer and DPoP tokens - ideal for gradual migration:
api_client = ApiClient(ApiClientOptions(
domain="<AUTH0_DOMAIN>",
audience="<AUTH0_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"
)Enforces DPoP-only authentication, rejecting Bearer tokens:
api_client = ApiClient(ApiClientOptions(
domain="<AUTH0_DOMAIN>",
audience="<AUTH0_AUDIENCE>",
dpop_required=True # Rejects Bearer tokens
))api_client = ApiClient(ApiClientOptions(
domain="<AUTH0_DOMAIN>",
audience="<AUTH0_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)
))We appreciate feedback and contribution to this repo! Before you get started, please read the following:
- Auth0's general contribution guidelines
- Auth0's code of conduct guidelines
- This repo's contribution guide
To provide feedback or report a bug, please raise an issue on our issue tracker.
Please do not report security vulnerabilities on the public GitHub issue tracker. The Responsible Disclosure Program details the procedure for disclosing security issues.

Auth0 is an easy to implement, adaptable authentication and authorization platform. To learn more checkout Why Auth0?
This project is licensed under the MIT license. See the LICENSE file for more info.