Core Library#
This page documents the core library (axioms-core-py) that axioms-flask-py depends on. These modules provide the underlying JWT validation, JWKS management, and configuration functionality.
Configuration#
Axioms configuration management.
- class AxiomsConfig(AXIOMS_DOMAIN=None, AXIOMS_ISS_URL=None, AXIOMS_AUDIENCE=None, AXIOMS_JWKS_URL=None, AXIOMS_TOKEN_TYPS=None, AXIOMS_SAFE_METHODS=None, AXIOMS_JWKS_REFRESH_INTERVAL=3600, AXIOMS_JWKS_CACHE_TTL=7200, AXIOMS_JWKS_PREFETCH=True, AXIOMS_SCOPE_CLAIMS=None, AXIOMS_ROLES_CLAIMS=None, AXIOMS_PERMISSIONS_CLAIMS=None)[source]#
Bases:
objectUnified configuration for Axioms authentication.
Centralizes all Axioms settings including auth configuration and JWKS manager settings. All configuration variables follow the AXIOMS_* naming convention.
Example
Basic:
from axioms_core.config import AxiomsConfig config = AxiomsConfig( AXIOMS_DOMAIN="auth.example.com", AXIOMS_AUDIENCE="my-api", AXIOMS_JWKS_REFRESH_INTERVAL=1800, # 30 minutes AXIOMS_JWKS_CACHE_TTL=3600, # 60 minutes ) # List all config values print(config.to_dict())
- Parameters:
- __init__(AXIOMS_DOMAIN=None, AXIOMS_ISS_URL=None, AXIOMS_AUDIENCE=None, AXIOMS_JWKS_URL=None, AXIOMS_TOKEN_TYPS=None, AXIOMS_SAFE_METHODS=None, AXIOMS_JWKS_REFRESH_INTERVAL=3600, AXIOMS_JWKS_CACHE_TTL=7200, AXIOMS_JWKS_PREFETCH=True, AXIOMS_SCOPE_CLAIMS=None, AXIOMS_ROLES_CLAIMS=None, AXIOMS_PERMISSIONS_CLAIMS=None)[source]#
Initialize Axioms configuration.
- Parameters:
AXIOMS_DOMAIN (str | None) – Auth provider domain (e.g., “auth.example.com”)
AXIOMS_ISS_URL (str | None) – Full issuer URL (overrides domain)
AXIOMS_AUDIENCE (str | None) – Expected audience for token validation
AXIOMS_JWKS_URL (str | None) – JWKS endpoint URL (overrides auto-discovery)
AXIOMS_TOKEN_TYPS (List[str] | None) – Allowed token types (default: [“JWT”, “jwt”, “at+jwt”, “application/jwt”])
AXIOMS_SAFE_METHODS (List[str] | None) – HTTP methods that bypass authorization checks (default: [“OPTIONS”] - commonly used for CORS preflight)
AXIOMS_JWKS_REFRESH_INTERVAL (int) – Seconds between JWKS refreshes (default: 3600)
AXIOMS_JWKS_CACHE_TTL (int) – Seconds before cache entry expires (default: 7200, must be >= 2x refresh interval)
AXIOMS_JWKS_PREFETCH (bool) – Whether to fetch JWKS on initialization (default: True)
AXIOMS_SCOPE_CLAIMS (List[str] | None) – List of claim names to check for scopes (default: [“scope”])
AXIOMS_ROLES_CLAIMS (List[str] | None) – List of claim names to check for roles (default: [“roles”])
AXIOMS_PERMISSIONS_CLAIMS (List[str] | None) – List of claim names to check for permissions (default: [“permissions”])
Helper Functions#
Framework-agnostic token validation and JWT verification for Axioms authentication.
This module handles JWT token validation, signature verification, JWKS key retrieval, and claim extraction. It supports configurable claim names to work with different authorization servers (AWS Cognito, Auth0, Okta, etc.).
This is the core module shared by axioms-fastapi, axioms-drf-py, and axioms-flask-py.
- validate_token_header(token, allowed_token_typs=None)[source]#
Validate JWT token header and extract algorithm and key ID.
Validates that the token header contains: - A valid algorithm from ALLOWED_ALGORITHMS (prevents algorithm confusion attacks) - A key ID (kid) for JWKS key lookup - A valid token type (typ) if present
- Parameters:
- Returns:
Token header containing ‘alg’, ‘kid’, and optionally ‘typ’.
- Return type:
- Raises:
AxiomsError – If token header is invalid, malformed, has invalid algorithm, is missing key ID, or has invalid token type.
- check_token_validity(token, key, alg, audience, issuer=None)[source]#
Check token validity including expiry, audience, and issuer.
Validates JWT token with comprehensive security checks: - Signature verification using JWKS public key - Algorithm validation (only secure asymmetric algorithms allowed) - Expiration time (exp claim must exist and be valid) - Audience (aud claim must match provided audience) - Issuer (iss claim validated if issuer provided) - Issued at time (iat claim) - Not before time (nbf claim if present)
Note
Token header validation (algorithm, kid, typ) should be performed separately using validate_token_header() before calling this function.
- Parameters:
- Returns:
Immutable (frozen) Box containing validated payload.
- Return type:
Box
- Raises:
AxiomsError – If token validation fails, with RFC 6750 compliant error details.
- check_claims(provided_claims, required_claims, operation='OR')[source]#
Generic function to check if required claims are present in provided claims.
This is the core authorization function used by check_scopes, check_roles, and check_permissions. It handles both string (space-separated) and list formats.
- Parameters:
provided_claims (str | List[str]) – Claims from the token. Can be: - Space-separated string (e.g., “read:data write:data”) - List of strings (e.g., [“admin”, “editor”])
required_claims (str | List[str]) – Required claims to check. Can be: - Space-separated string (e.g., “read:data write:data”) - List of strings (e.g., [“admin”, “editor”])
operation (str) – Authorization operation - “OR” (any one required) or “AND” (all required). Defaults to “OR”. Case-insensitive.
- Returns:
- True if authorization check passes based on operation:
OR: True if any required claim is present (intersection check)
AND: True if all required claims are present (subset check)
- Return type:
Example
Basic:
# With space-separated strings check_claims("read:data write:data", "read:data admin") # True (OR) check_claims("read:data", "read:data write:data", "AND") # False # With lists check_claims(["admin", "editor"], ["admin"]) # True (OR) check_claims(["admin"], ["admin", "editor"], "AND") # False # Mixed formats check_claims("read:data write:data", ["read:data", "write:data"], "AND") # True
- check_scopes(provided_scopes, required_scopes, operation='OR')[source]#
Check if required scopes are present in provided scopes.
This is a convenience wrapper around check_claims() for scope checking.
- Parameters:
- Returns:
True if authorization check passes based on operation.
- Return type:
Example
Basic:
check_scopes("read:data write:data", ["read:data", "admin"]) # True check_scopes("read:data write:data", ["read:data", "write:data"], "AND") # True
- check_roles(provided_roles, required_roles, operation='OR')[source]#
Check if required roles are present in provided roles.
This is a convenience wrapper around check_claims() for role checking.
- Parameters:
- Returns:
True if authorization check passes based on operation.
- Return type:
Example
Basic:
check_roles(["editor", "viewer"], ["admin", "editor"]) # True check_roles(["admin", "editor"], ["admin", "editor"], "AND") # True
- check_permissions(provided_permissions, required_permissions, operation='OR')[source]#
Check if required permissions are present in provided permissions.
This is a convenience wrapper around check_claims() for permission checking.
- Parameters:
- Returns:
True if authorization check passes based on operation.
- Return type:
Example
Basic:
perms = ["users:read", "users:write"] check_permissions(perms, ["users:write", "users:read"]) # True check_permissions(perms, perms, "AND") # True
- get_claim_names(claim_type, config=None)[source]#
Get list of claim names to check for a given claim type.
Checks configuration for custom claim names, falling back to defaults.
- Parameters:
- Returns:
List of claim names to check in priority order.
- Return type:
Example
Basic:
get_claim_names('ROLES') # Returns: ['roles'] config = AxiomsConfig(AXIOMS_ROLES_CLAIMS=['role', 'roles']) get_claim_names('ROLES', config) # Returns: ['role', 'roles']
- get_claim_from_token(payload, claim_type, config=None)[source]#
Extract claim value from token payload.
Checks multiple possible claim names based on configuration, returning the first non-None value found. Handles both string and list/tuple formats.
- Parameters:
- Returns:
The claim value if found, None otherwise. For SCOPE claims in list/tuple format, returns a space-separated string.
- Return type:
Example
Basic:
get_claim_from_token(payload, 'ROLES') # Returns: ['admin', 'editor'] or ('admin', 'editor') for frozen Box get_claim_from_token(payload, 'SCOPE') # Returns: 'openid profile' (converted from list/tuple if needed)
- get_token_scopes(payload, config=None)[source]#
Extract scopes from token payload as space-separated string.
- Parameters:
- Returns:
Space-separated string of scopes, or None if not found.
- Return type:
str | None
Example
Basic:
get_token_scopes(payload) # Returns: 'openid profile email'
- get_token_roles(payload, config=None)[source]#
Extract roles from token payload as list.
- Parameters:
- Returns:
List of roles, or None if not found.
- Return type:
Example
Basic:
get_token_roles(payload) # Returns: ['admin', 'editor']
- get_token_permissions(payload, config=None)[source]#
Extract permissions from token payload as list.
- Parameters:
- Returns:
List of permissions, or None if not found.
- Return type:
Example
Basic:
get_token_permissions(payload) # Returns: ['users:read', 'users:write']
- get_expected_issuer(config=None)[source]#
Get expected issuer URL from configuration.
Checks for AXIOMS_ISS_URL first, then constructs from AXIOMS_DOMAIN. The issuer is used to validate the ‘iss’ claim in JWT tokens.
- Parameters:
config (Dict[str, Any] | Any | None) – Optional configuration dict or object.
- Returns:
- Expected issuer URL (e.g., ‘https://auth.example.com’),
or None if neither AXIOMS_ISS_URL nor AXIOMS_DOMAIN is configured.
- Return type:
str or None
Example
Basic:
config = AxiomsConfig( AXIOMS_ISS_URL="https://auth.example.com/oauth2" ) get_expected_issuer(config) # Returns: 'https://auth.example.com/oauth2' config = AxiomsConfig( AXIOMS_DOMAIN="auth.example.com" ) get_expected_issuer(config) # Returns: 'https://auth.example.com'
- get_jwks_url(config=None)[source]#
Get JWKS URL from configuration.
Checks for AXIOMS_JWKS_URL first, then constructs URL from AXIOMS_ISS_URL. If AXIOMS_ISS_URL is not set, it will be derived from AXIOMS_DOMAIN.
- Configuration hierarchy:
AXIOMS_JWKS_URL (if set, used directly)
AXIOMS_ISS_URL + /.well-known/jwks.json
https://{AXIOMS_DOMAIN} + /.well-known/jwks.json (via AXIOMS_ISS_URL)
- get_key_from_jwks_json(kid, config=None)[source]#
Retrieve public key from JWKS endpoint for token verification.
Uses the global JWKS manager for caching and background refresh if initialized. If the manager is not initialized, falls back to on-demand fetching with warning.
- For best performance, initialize the JWKS manager on application startup:
Sync frameworks: Use initialize_jwks_manager()
Async frameworks: Use initialize_async_jwks_manager()
JWKS Manager#
JWKS (JSON Web Key Set) manager for Axioms authentication.
This module provides both sync and async JWKS managers: - JWKSManager: Thread-based manager for Flask, Django (WSGI), and other sync frameworks - AsyncJWKSManager: Asyncio-based manager for FastAPI, Django (ASGI), and other async frameworks
- class JWKSManager[source]#
Bases:
objectThread-safe JWKS manager with background refresh support.
This manager handles JWKS fetching with: - HTTP requests using httpx (sync mode for framework compatibility) - Periodic background refresh using threading - In-memory caching with TTL - Thread-safe access - Framework-agnostic (works with FastAPI, Django, Flask)
The manager can be initialized on application startup or will lazy-initialize on first use with a blocking fetch followed by background refreshes.
- class AsyncJWKSManager[source]#
Bases:
objectAsync JWKS manager with background refresh support for async applications.
This manager handles JWKS fetching with: - HTTP requests using httpx.AsyncClient - Periodic background refresh using asyncio - In-memory caching with TTL - Async-safe access - For FastAPI, Django (ASGI), and other async frameworks
The manager should be initialized on application startup.
- initialize_jwks_manager(config=None, jwks_url=None, refresh_interval=3600, cache_ttl=7200, prefetch=True)[source]#
Initialize the global JWKS manager for sync/threading-based frameworks.
This should be called during application startup to enable background JWKS refresh and avoid blocking requests. Uses threading for background refresh.
Use this for: - Flask (WSGI mode - the default, even with async route handlers) - Django WSGI - Any framework using threading/WSGI
For truly async frameworks using asyncio event loops: - FastAPI: Use initialize_async_jwks_manager - Django ASGI: Use initialize_async_jwks_manager - Flask (ASGI mode with Hypercorn/Uvicorn): Use initialize_async_jwks_manager
Note: Flask 2.0+ supports async/await in route handlers but still runs on WSGI by default (using thread pools). Only use initialize_async_jwks_manager if running Flask on an ASGI server like Hypercorn.
- Parameters:
config (AxiomsConfig | None) – AxiomsConfig object (recommended). If provided, uses config values by default.
jwks_url (str | None) – JWKS URL to fetch. If None and config provided, uses config.AXIOMS_JWKS_URL.
refresh_interval (int) – Interval in seconds between refresh attempts (default: 3600).
cache_ttl (int) – Cache TTL in seconds (default: 7200, must be >= 2x refresh_interval).
prefetch (bool) – If True, pre-fetch JWKS before starting background refresh.
Example
Flask-WSGI:
from axioms_core import AxiomsConfig, initialize_jwks_manager, shutdown_jwks_manager from flask import Flask app = Flask(__name__) config = AxiomsConfig( AXIOMS_JWKS_URL="https://auth.example.com/.well-known/jwks.json", AXIOMS_JWKS_REFRESH_INTERVAL=1800, AXIOMS_JWKS_CACHE_TTL=3600, ) @app.before_first_request def startup(): initialize_jwks_manager(config=config) # Shutdown automatically called via atexit, or manually: @app.teardown_appcontext def shutdown(exception=None): shutdown_jwks_manager()
Example
Django-WSGI:
# In apps.py from django.apps import AppConfig from axioms_core import initialize_jwks_manager class MyAppConfig(AppConfig): def ready(self): initialize_jwks_manager( jwks_url="https://auth.example.com/.well-known/jwks.json", refresh_interval=1800, cache_ttl=3600 )
- shutdown_jwks_manager()[source]#
Shutdown the global JWKS manager.
This should be called during application shutdown to cleanup resources. It’s also automatically called via atexit registration.
- async initialize_async_jwks_manager(config=None, jwks_url=None, refresh_interval=3600, cache_ttl=7200, prefetch=True)[source]#
Initialize the global async JWKS manager for asyncio-based frameworks.
This should be called during application startup to enable background JWKS refresh and avoid blocking requests. Uses asyncio for background refresh.
Use this for frameworks running on asyncio event loops: - FastAPI (always uses asyncio) - Django ASGI (async mode) - Flask on ASGI servers (Hypercorn, Uvicorn, etc.) - Any ASGI application
For threading-based frameworks, use initialize_jwks_manager instead: - Flask (WSGI mode - the default) - Django WSGI
Note: Flask 2.0+ supports async/await syntax but runs on WSGI by default. Only use this function if running Flask on an ASGI server like Hypercorn.
- Parameters:
config (AxiomsConfig | None) – AxiomsConfig object (recommended). If provided, uses config values by default.
jwks_url (str | None) – JWKS URL to fetch. If None and config provided, uses config.AXIOMS_JWKS_URL.
refresh_interval (int) – Interval in seconds between refresh attempts (default: 3600).
cache_ttl (int) – Cache TTL in seconds (default: 7200, must be >= 2x refresh_interval).
prefetch (bool) – If True, pre-fetch JWKS before starting background refresh.
Example
with FastAPI (lifespan context manager):
from contextlib import asynccontextmanager from fastapi import FastAPI from axioms_core import ( AxiomsConfig, initialize_async_jwks_manager, shutdown_async_jwks_manager ) config = AxiomsConfig( AXIOMS_JWKS_URL="https://auth.example.com/.well-known/jwks.json", AXIOMS_JWKS_REFRESH_INTERVAL=1800, AXIOMS_JWKS_CACHE_TTL=3600, ) @asynccontextmanager async def lifespan(app: FastAPI): # Startup await initialize_async_jwks_manager(config=config) yield # Shutdown await shutdown_async_jwks_manager() app = FastAPI(lifespan=lifespan)
Example
FastAPI with startup/shutdown events:
from fastapi import FastAPI from axioms_core import initialize_async_jwks_manager, shutdown_async_jwks_manager app = FastAPI() @app.on_event("startup") async def startup(): await initialize_async_jwks_manager( jwks_url="https://auth.example.com/.well-known/jwks.json" ) @app.on_event("shutdown") async def shutdown(): await shutdown_async_jwks_manager()
Example
Django-ASGI:
# In asgi.py from django.core.asgi import get_asgi_application from axioms_core import initialize_async_jwks_manager import asyncio # Initialize JWKS manager before application starts asyncio.run(initialize_async_jwks_manager( jwks_url="https://auth.example.com/.well-known/jwks.json", refresh_interval=1800, cache_ttl=3600 )) application = get_asgi_application()
Errors#
Axioms error classes following RFC 6750 OAuth 2.0 Bearer Token standard.
- exception AxiomsError(error, status_code=401)[source]#
Bases:
ExceptionBase exception for Axioms errors following RFC 6750.
Standard OAuth 2.0 Bearer Token error codes (RFC 6750): - invalid_request (HTTP 400): Missing/invalid parameters, malformed request - invalid_token (HTTP 401): Expired, revoked, malformed, or invalid token - insufficient_scope (HTTP 403): Token lacks required permissions/scopes - server_error (HTTP 500): Server configuration or internal errors
- Parameters: