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: object

Unified 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:
  • AXIOMS_DOMAIN (str | None)

  • AXIOMS_ISS_URL (str | None)

  • AXIOMS_AUDIENCE (str | None)

  • AXIOMS_JWKS_URL (str | None)

  • AXIOMS_TOKEN_TYPS (List[str] | None)

  • AXIOMS_SAFE_METHODS (List[str] | None)

  • AXIOMS_JWKS_REFRESH_INTERVAL (int)

  • AXIOMS_JWKS_CACHE_TTL (int)

  • AXIOMS_JWKS_PREFETCH (bool)

  • AXIOMS_SCOPE_CLAIMS (List[str] | None)

  • AXIOMS_ROLES_CLAIMS (List[str] | None)

  • AXIOMS_PERMISSIONS_CLAIMS (List[str] | None)

__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”])

to_dict()[source]#

Convert configuration to dictionary.

Returns:

Dictionary containing all configuration values.

Return type:

Dict[str, Any]

__repr__()[source]#

String representation of config.

Return type:

str

get_config_value(config, key, default=None)[source]#

Get configuration value from config object.

Supports both dict-like and object attribute access patterns.

Parameters:
  • config (Any | None) – Configuration object or dict.

  • key (str) – Configuration key to retrieve.

  • default (Any) – Default value if key not found.

Returns:

Configuration value or default.

Return type:

Any

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:
  • token (str) – JWT token string to validate.

  • allowed_token_typs (List[str] | None) – Optional list of allowed token types. Defaults to [“JWT”, “jwt”, “at+jwt”, “application/jwt”].

Returns:

Token header containing ‘alg’, ‘kid’, and optionally ‘typ’.

Return type:

dict

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:
  • token (str) – JWT token string to validate.

  • key – JWK key for verification.

  • alg (str) – Algorithm from token header (already validated against ALLOWED_ALGORITHMS).

  • audience (str) – Expected audience value.

  • issuer (str | None) – Optional expected issuer value.

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:

bool

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:
  • provided_scopes (str) – Space-separated string of scopes from the token.

  • required_scopes (List[str]) – List of required scope strings.

  • operation (str) – Authorization operation - “OR” (any one required) or “AND” (all required). Defaults to “OR”.

Returns:

True if authorization check passes based on operation.

Return type:

bool

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:
  • provided_roles (List[str]) – List of roles from the token.

  • required_roles (List[str]) – List of required role strings.

  • operation (str) – Authorization operation - “OR” (any one required) or “AND” (all required). Defaults to “OR”.

Returns:

True if authorization check passes based on operation.

Return type:

bool

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:
  • provided_permissions (List[str]) – List of permissions from the token.

  • required_permissions (List[str]) – List of required permission strings.

  • operation (str) – Authorization operation - “OR” (any one required) or “AND” (all required). Defaults to “OR”.

Returns:

True if authorization check passes based on operation.

Return type:

bool

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:
  • claim_type (str) – Type of claim (‘SCOPE’, ‘ROLES’, or ‘PERMISSIONS’).

  • config (Dict[str, Any] | Any | None) – Optional configuration dict or object.

Returns:

List of claim names to check in priority order.

Return type:

list

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:
  • payload (Box) – Decoded JWT token payload (Box object).

  • claim_type (str) – Type of claim (‘SCOPE’, ‘ROLES’, or ‘PERMISSIONS’).

  • config (Dict[str, Any] | Any | None) – Optional configuration dict or object.

Returns:

The claim value if found, None otherwise. For SCOPE claims in list/tuple format, returns a space-separated string.

Return type:

Any

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:
  • payload (Box) – Decoded JWT token payload (Box object).

  • config (Dict[str, Any] | Any | None) – Optional configuration dict or object.

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:
  • payload (Box) – Decoded JWT token payload (Box object).

  • config (Dict[str, Any] | Any | None) – Optional configuration dict or object.

Returns:

List of roles, or None if not found.

Return type:

List[str] | None

Example

Basic:

get_token_roles(payload)
# Returns: ['admin', 'editor']
get_token_permissions(payload, config=None)[source]#

Extract permissions from token payload as list.

Parameters:
  • payload (Box) – Decoded JWT token payload (Box object).

  • config (Dict[str, Any] | Any | None) – Optional configuration dict or object.

Returns:

List of permissions, or None if not found.

Return type:

List[str] | None

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:
  1. AXIOMS_JWKS_URL (if set, used directly)

  2. AXIOMS_ISS_URL + /.well-known/jwks.json

  3. https://{AXIOMS_DOMAIN} + /.well-known/jwks.json (via AXIOMS_ISS_URL)

Parameters:

config (Dict[str, Any] | Any | None) – Optional configuration dict or object.

Returns:

Full JWKS URL.

Return type:

str

Raises:

Exception – If JWKS URL cannot be determined from configuration.

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()

Parameters:
  • kid (str) – Key ID from the JWT header.

  • config (Dict[str, Any] | Any | None) – Optional configuration dict or object.

Returns:

JSON Web Key for signature verification.

Return type:

JWK

Raises:

AxiomsError – If key cannot be retrieved or is invalid.

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: object

Thread-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.

static __new__(cls)[source]#

Singleton pattern to ensure only one manager instance.

__init__()[source]#

Initialize the JWKS manager.

initialize(jwks_url, refresh_interval=3600, cache_ttl=600, prefetch=True)[source]#

Initialize the manager and start background refresh.

Parameters:
  • jwks_url (str) – JWKS URL to fetch.

  • refresh_interval (int) – Interval in seconds between refresh attempts (default: 3600).

  • cache_ttl (int) – Cache TTL in seconds (default: 600).

  • prefetch (bool) – If True, pre-fetch JWKS before starting background refresh.

shutdown()[source]#

Shutdown the manager and cleanup resources.

get_jwks(url)[source]#

Get JWKS data from cache or fetch if needed.

This method is thread-safe and can be called from any context.

Parameters:

url (str) – JWKS URL.

Returns:

JWKS data.

Return type:

bytes

class AsyncJWKSManager[source]#

Bases: object

Async 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.

static __new__(cls)[source]#

Singleton pattern to ensure only one manager instance.

__init__()[source]#

Initialize the async JWKS manager.

async initialize(jwks_url, refresh_interval=3600, cache_ttl=7200, prefetch=True)[source]#

Initialize the manager and start background refresh.

Parameters:
  • jwks_url (str) – JWKS URL to fetch.

  • refresh_interval (int) – Interval in seconds between refresh attempts (default: 3600).

  • cache_ttl (int) – Cache TTL in seconds (default: 7200).

  • prefetch (bool) – If True, pre-fetch JWKS before starting background refresh.

async shutdown()[source]#

Shutdown the manager and cleanup resources.

async get_jwks(url)[source]#

Get JWKS data from cache or fetch if needed.

This method is async-safe and can be called from async contexts.

Parameters:

url (str) – JWKS URL.

Returns:

JWKS data.

Return type:

bytes

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()
async shutdown_async_jwks_manager()[source]#

Shutdown the global async JWKS manager.

This should be called during application shutdown to cleanup resources.

Errors#

Axioms error classes following RFC 6750 OAuth 2.0 Bearer Token standard.

exception AxiomsError(error, status_code=401)[source]#

Bases: Exception

Base 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:
  • error (Dict[str, str]) – Error details dict with ‘error’ and ‘error_description’ keys per RFC 6750.

  • status_code (int) – HTTP status code.

__init__(error, status_code=401)[source]#
Parameters: