API Reference#

This page contains the full API reference for axioms-fastapi, automatically generated from the source code docstrings.

Core Configuration#

The SDK requires the following environment variables to be configured:

Parameter

Required

Description

AXIOMS_AUDIENCE

Yes

Expected audience claim in the JWT token.

AXIOMS_DOMAIN

No

Axioms domain name. Used as the base to construct AXIOMS_ISS_URL if not explicitly provided. This is the simplest configuration option for standard OAuth2/OIDC providers.

AXIOMS_ISS_URL

No

Full issuer URL for validating the iss claim in JWT tokens (e.g., https://auth.example.com/oauth2). If not provided, constructed as https://{AXIOMS_DOMAIN}. Used to construct AXIOMS_JWKS_URL if that is not explicitly set. Recommended for security to prevent token substitution attacks.

AXIOMS_JWKS_URL

No

Full URL to JWKS endpoint (e.g., https://auth.example.com/.well-known/jwks.json). If not provided, constructed as {AXIOMS_ISS_URL}/.well-known/jwks.json

Important

Either AXIOMS_JWKS_URL, AXIOMS_ISS_URL, or AXIOMS_DOMAIN must be configured for token validation.

Configuration Hierarchy:

The SDK uses the following construction order:

  1. AXIOMS_DOMAIN → constructs → AXIOMS_ISS_URL (if not explicitly set)

  2. AXIOMS_ISS_URL → constructs → AXIOMS_JWKS_URL (if not explicitly set)

Example: Setting only AXIOMS_DOMAIN=auth.example.com/oauth results in:

  • AXIOMS_ISS_URL: https://auth.example.com/oauth

  • AXIOMS_JWKS_URL: https://auth.example.com/oauth/.well-known/jwks.json

Security & Algorithm Validation#

The SDK implements multiple security best practices to prevent common JWT attacks:

Algorithm Validation

Only secure asymmetric algorithms are accepted for JWT signature verification. The SDK validates that:

  1. The alg header in the JWT specifies an allowed algorithm

  2. Each key is used with exactly one algorithm

  3. The algorithm validation occurs before cryptographic operations

Supported Algorithms:

  • RSA: RS256, RS384, RS512

  • ECDSA: ES256, ES384, ES512

  • RSA-PSS: PS256, PS384, PS512

Rejected Algorithms:

  • none - No signature (critical security vulnerability)

  • HS256, HS384, HS512 - Symmetric algorithms (prevents key confusion attacks)

  • Any algorithm not in the allowed list above

This prevents algorithm confusion attacks where an attacker might try to:

  • Use the none algorithm to bypass signature verification

  • Substitute an asymmetric algorithm with a symmetric one

  • Use weak or deprecated algorithms

Additional Security Features:

  • Issuer validation (iss claim) to prevent token substitution

  • Automatic public key retrieval and validation from JWKS endpoints

  • Token expiration validation

  • Audience claim validation

  • Key ID (kid) validation

Claim Name Mapping#

Configure custom claim names to support different authorization servers (AWS Cognito, Auth0, Okta, Microsoft Entra). These mapping options provide additional customization for claim names used to extract scopes, roles, and permissions from the JWT token. These mappings can also support RFC 9068 JWT Profile for OAuth 2.0 Access Tokens.

Parameter

Required

Description

AXIOMS_SCOPE_CLAIMS

No

List of scope claim names to check in priority order.

Default: ['scope']

Example: ['scope', 'scp']

AXIOMS_ROLES_CLAIMS

No

List of role claim names to check in priority order.

Default: ['roles']

Example: ['roles', 'cognito:roles']

AXIOMS_PERMISSIONS_CLAIMS

No

List of permission claim names to check in priority order.

Default: ['permissions']

Example: ['permissions', 'cognito:groups', 'groups', 'entitlements']

Important

Namespaced Claims: You can specify namespaced claim names directly in the claim configuration lists.

The SDK will check claims in the order you specify them, using the first non-None value found.

Example: AXIOMS_ROLES_CLAIMS = ['roles', 'https://myapp.com/claims/roles', 'cognito:groups']

Setting Environment Variables#

You can set these environment variables using a .env file with python-dotenv:

  1. Create a .env file in your project root

  2. Add your configuration:

    # Required
    AXIOMS_AUDIENCE=your-api-audience-or-resource-identifier
    
    # Option 1: Use AXIOMS_DOMAIN
    AXIOMS_DOMAIN=your-domain.axioms.io
    
    # Option 2: Use AXIOMS_JWKS_URL (takes precedence)
    # AXIOMS_JWKS_URL=https://my-auth.domain.com/oauth2/.well-known/jwks.json
    
  3. Load the environment variables in your FastAPI app:

    from fastapi import FastAPI
    from dotenv import load_dotenv
    import os
    
    load_dotenv()
    
    app = FastAPI()
    

Alternatively, you can set environment variables directly in your application or deployment environment.

import os

os.environ['AXIOMS_AUDIENCE'] = 'your-api-audience'
os.environ['AXIOMS_JWKS_URL'] = 'https://my-auth.domain.com/oauth2/.well-known/jwks.json'

Dependencies#

The dependencies module provides FastAPI dependency functions for authentication and authorization.

FastAPI dependencies for authentication and authorization.

This module provides FastAPI dependency functions for protecting routes with JWT-based authentication and authorization. Supports scope-based, role-based, permission-based, and object-level ownership access control with configurable claim names for different authorization servers.

Example:

from fastapi import FastAPI, Depends
from axioms_fastapi import require_auth, require_scopes, check_object_ownership, init_axioms

app = FastAPI()
init_axioms(app, AXIOMS_AUDIENCE="api.example.com", AXIOMS_DOMAIN="auth.example.com")

@app.get("/protected")
async def protected_route(payload=Depends(require_auth)):
    return {"user": payload.sub}

@app.get("/admin")
async def admin_route(payload=Depends(require_auth), _=Depends(require_scopes(["admin"]))):
    return {"message": "Admin access"}

@app.patch("/articles/{article_id}")
async def update_article(
    article_id: int,
    article=Depends(check_object_ownership(get_article))
):
    return {"message": "Updated"}
require_auth(request, config=Depends(dependency=<function get_config>, use_cache=True, scope=None), safe_methods=None)[source]#

FastAPI dependency to require valid JWT authentication.

Validates the JWT access token in the Authorization header and returns the validated payload for use in the route handler.

Parameters:
  • request (Request) – FastAPI Request object containing HTTP headers.

  • config (AxiomsConfig) – Axioms configuration (injected via dependency).

  • safe_methods (List[str]) – List of HTTP methods that skip authentication. Defaults to [‘OPTIONS’] for CORS preflight requests.

Returns:

Validated JWT token payload with claims accessible as attributes.

Returns empty Box for safe methods.

Return type:

Box

Raises:

AxiomsHTTPException – If token is missing, invalid, or expired (not raised for safe methods).

Example:

@app.get("/api/protected")
async def protected_route(payload=Depends(require_auth)):
    user_id = payload.sub
    return {"user_id": user_id}

Example with custom safe methods:

from functools import partial

# Allow GET and OPTIONS without auth
require_auth_safe = partial(require_auth, safe_methods=["GET", "OPTIONS"])

@app.get("/api/public-read")
async def public_route(payload=Depends(require_auth_safe)):
    # Returns empty Box for GET requests
    return {"data": "public"}
require_scopes(required_scopes)[source]#

Create a FastAPI dependency to enforce scope-based authorization.

Checks if the authenticated user’s token contains any of the required scopes. Uses OR logic: the token must have at least ONE of the specified scopes.

Parameters:

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

Returns:

FastAPI dependency function that enforces scope check.

Return type:

Callable

Raises:

AxiomsHTTPException – If token doesn’t contain required scopes.

Example (OR logic - requires EITHER scope):

@app.get("/api/resource")
async def resource_route(
    payload=Depends(require_auth),
    _=Depends(require_scopes(["read:resource", "write:resource"]))
):
    return {"data": "protected"}

Example (AND logic - requires BOTH scopes via chaining):

@app.get("/api/strict")
async def strict_route(
    payload=Depends(require_auth),
    _=Depends(require_scopes(["read:resource"])),
    __=Depends(require_scopes(["write:resource"]))
):
    return {"data": "requires both scopes"}
require_roles(required_roles)[source]#

Create a FastAPI dependency to enforce role-based authorization.

Checks if the authenticated user’s token contains any of the required roles. Uses OR logic: the token must have at least ONE of the specified roles.

Parameters:

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

Returns:

FastAPI dependency function that enforces role check.

Return type:

Callable

Raises:

AxiomsHTTPException – If token doesn’t contain required roles.

Example (OR logic - requires EITHER role):

@app.get("/admin/users")
async def admin_route(
    payload=Depends(require_auth),
    _=Depends(require_roles(["admin", "superuser"]))
):
    return {"users": []}

Example (AND logic - requires BOTH roles via chaining):

@app.get("/admin/critical")
async def critical_route(
    payload=Depends(require_auth),
    _=Depends(require_roles(["admin"])),
    __=Depends(require_roles(["superuser"]))
):
    return {"message": "requires both roles"}
require_permissions(required_permissions)[source]#

Create a FastAPI dependency to enforce permission-based authorization.

Checks if the authenticated user’s token contains any of the required permissions. Uses OR logic: the token must have at least ONE of the specified permissions.

Parameters:

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

Returns:

FastAPI dependency function that enforces permission check.

Return type:

Callable

Raises:

AxiomsHTTPException – If token doesn’t contain required permissions.

Example (OR logic - requires EITHER permission):

@app.get("/api/resource")
async def resource_route(
    payload=Depends(require_auth),
    _=Depends(require_permissions(["resource:read", "resource:write"]))
):
    return {"data": "success"}

Example (AND logic - requires BOTH permissions via chaining):

@app.get("/api/critical")
async def critical_route(
    payload=Depends(require_auth),
    _=Depends(require_permissions(["resource:read"])),
    __=Depends(require_permissions(["resource:admin"]))
):
    return {"message": "requires both permissions"}
check_object_ownership(get_object, owner_field='user', claim_field='sub')[source]#

Create a FastAPI dependency to enforce object-level ownership permissions.

Validates that the authenticated user owns the requested object by comparing a field in the object with a claim in the JWT token. This enables per-object access control where users can only access resources they own.

Parameters:
  • get_object (Callable) – Callable dependency function that retrieves the object.

  • owner_field (str) – Field name in the object containing the owner identifier. Defaults to “user”.

  • claim_field (str) – JWT claim field to compare with owner_field. Defaults to “sub”.

Returns:

FastAPI dependency function that enforces object ownership check.

Return type:

Callable

Raises:

AxiomsHTTPException

  • 400 Bad Request: If object is missing the specified owner_field. - 403 Forbidden: If JWT is missing the claim_field or user doesn’t own the object.

Example (basic usage with defaults):

async def get_article(article_id: int):
    article = db.query(Article).filter(Article.id == article_id).first()
    if not article:
        raise HTTPException(status_code=404, detail="Not found")
    return article

@app.patch("/articles/{article_id}")
async def update_article(
    article_id: int,
    title: str,
    article = Depends(check_object_ownership(get_article))
):
    article.title = title
    return article

Example (custom owner field):

@app.delete("/comments/{comment_id}")
async def delete_comment(
    comment_id: int,
    comment = Depends(check_object_ownership(get_comment, owner_field="created_by"))
):
    db.delete(comment)
    return {"message": "Deleted"}

Example (match by email instead of sub):

@app.patch("/users/{user_id}")
async def update_user(
    user_id: int,
    name: str,
    user = Depends(check_object_ownership(
        get_user,
        owner_field="owner_email",
        claim_field="email"
    ))
):
    user.name = name
    return user

Example (with SQLAlchemy):

class Article(Base):
    __tablename__ = "articles"
    id = Column(Integer, primary_key=True)
    title = Column(String)
    user = Column(String)

def get_article(article_id: int, db: Session = Depends(get_db)):
    article = db.query(Article).filter(Article.id == article_id).first()
    if not article:
        raise HTTPException(status_code=404, detail="Not found")
    return article

@app.patch("/articles/{article_id}")
async def update_article(
    article_id: int,
    title: str,
    article: Article = Depends(check_object_ownership(get_article)),
    db: Session = Depends(get_db)
):
    article.title = title
    db.commit()
    return article

Token Validation#

The token module handles JWT token validation and verification.

Error Handling#

The error module defines custom exceptions for authentication and authorization errors.

Error handling for Axioms FastAPI authentication and authorization.

This module defines custom exceptions for authentication and authorization errors in FastAPI applications.

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

Bases: Exception

Base exception for Axioms authentication and authorization errors.

Parameters:
  • error (Dict[str, str]) – Dictionary containing error details with ‘error’ and ‘error_description’ keys.

  • status_code (int) – HTTP status code for the error (default: 401).

Example:

raise AxiomsError(
    {"error": "unauthorized_access", "error_description": "Invalid token"},
    401
)
__init__(error, status_code=401)[source]#

Initialize AxiomsError with error details and status code.

Parameters:
exception AxiomsHTTPException(error, status_code=401, realm=None)[source]#

Bases: HTTPException

FastAPI HTTP exception for Axioms errors.

This exception is compatible with FastAPI’s exception handling system. It includes WWW-Authenticate header for 401 and 403 responses.

Parameters:
  • error (Dict[str, str]) – Dictionary containing error details.

  • status_code (int) – HTTP status code (default: 401).

  • realm (str) – Optional realm (issuer URL) for WWW-Authenticate header.

Example:

raise AxiomsHTTPException(
    {"error": "invalid_token", "error_description": "Token expired"},
    401,
    "https://auth.example.com"
)

Note

  • 401 responses: Authentication failure (missing/invalid token)

  • 403 responses: Authorization failure (insufficient permissions)

__init__(error, status_code=401, realm=None)[source]#

Initialize AxiomsHTTPException with error details.

Parameters:
register_axioms_exception_handler(app)[source]#

Register the Axioms exception handler with the FastAPI application.

This convenience function registers a default exception handler for AxiomsHTTPException exceptions. The handler returns appropriate HTTP status codes and includes the WWW-Authenticate header for 401 and 403 responses.

Parameters:

app (FastAPI) – FastAPI application instance.

Return type:

None

Example:

from fastapi import FastAPI
from axioms_fastapi.error import register_axioms_exception_handler

app = FastAPI()
register_axioms_exception_handler(app)

Note

The exception handler is already configured in the AxiomsHTTPException class with proper headers and status codes. This function simply registers a handler that returns the exception details in a JSON response.

Configuration#

The config module provides configuration management for the SDK.

Configuration management for Axioms FastAPI.

This module provides configuration classes for managing Axioms settings in FastAPI applications.

class AxiomsConfig(_case_sensitive=None, _nested_model_default_partial_update=None, _env_prefix=None, _env_file=PosixPath('.'), _env_file_encoding=None, _env_ignore_empty=None, _env_nested_delimiter=None, _env_nested_max_split=None, _env_parse_none_str=None, _env_parse_enums=None, _cli_prog_name=None, _cli_parse_args=None, _cli_settings_source=None, _cli_parse_none_str=None, _cli_hide_none_type=None, _cli_avoid_json=None, _cli_enforce_required=None, _cli_use_class_docs_for_groups=None, _cli_exit_on_error=None, _cli_prefix=None, _cli_flag_prefix_char=None, _cli_implicit_flags=None, _cli_ignore_unknown_args=None, _cli_kebab_case=None, _cli_shortcuts=None, _secrets_dir=None, *, AXIOMS_AUDIENCE, AXIOMS_DOMAIN=None, AXIOMS_ISS_URL=None, AXIOMS_JWKS_URL=None, AXIOMS_SCOPE_CLAIMS=None, AXIOMS_ROLES_CLAIMS=None, AXIOMS_PERMISSIONS_CLAIMS=None, **values)[source]#

Bases: BaseSettings

Configuration for Axioms FastAPI authentication.

All settings can be set via environment variables with the AXIOMS_ prefix.

Example:

config = AxiomsConfig(
    AXIOMS_AUDIENCE="api.example.com",
    AXIOMS_DOMAIN="auth.example.com"
)
Parameters:
  • _case_sensitive (bool | None)

  • _nested_model_default_partial_update (bool | None)

  • _env_prefix (str | None)

  • _env_file (DotenvType | None)

  • _env_file_encoding (str | None)

  • _env_ignore_empty (bool | None)

  • _env_nested_delimiter (str | None)

  • _env_nested_max_split (int | None)

  • _env_parse_none_str (str | None)

  • _env_parse_enums (bool | None)

  • _cli_prog_name (str | None)

  • _cli_parse_args (bool | list[str] | tuple[str, ...] | None)

  • _cli_settings_source (CliSettingsSource[Any] | None)

  • _cli_parse_none_str (str | None)

  • _cli_hide_none_type (bool | None)

  • _cli_avoid_json (bool | None)

  • _cli_enforce_required (bool | None)

  • _cli_use_class_docs_for_groups (bool | None)

  • _cli_exit_on_error (bool | None)

  • _cli_prefix (str | None)

  • _cli_flag_prefix_char (str | None)

  • _cli_implicit_flags (bool | None)

  • _cli_ignore_unknown_args (bool | None)

  • _cli_kebab_case (bool | Literal['all', 'no_enums'] | None)

  • _cli_shortcuts (Mapping[str, str | list[str]] | None)

  • _secrets_dir (PathType | None)

  • AXIOMS_AUDIENCE (str)

  • AXIOMS_DOMAIN (str | None)

  • AXIOMS_ISS_URL (str | None)

  • AXIOMS_JWKS_URL (str | None)

  • AXIOMS_SCOPE_CLAIMS (List[str] | None)

  • AXIOMS_ROLES_CLAIMS (List[str] | None)

  • AXIOMS_PERMISSIONS_CLAIMS (List[str] | None)

  • values (Any)

AXIOMS_AUDIENCE: str#
AXIOMS_DOMAIN: str | None#
AXIOMS_ISS_URL: str | None#
AXIOMS_JWKS_URL: str | None#
AXIOMS_SCOPE_CLAIMS: List[str] | None#
AXIOMS_ROLES_CLAIMS: List[str] | None#
AXIOMS_PERMISSIONS_CLAIMS: List[str] | None#
model_config: ClassVar[SettingsConfigDict] = {'arbitrary_types_allowed': True, 'case_sensitive': True, 'cli_avoid_json': False, 'cli_enforce_required': False, 'cli_exit_on_error': True, 'cli_flag_prefix_char': '-', 'cli_hide_none_type': False, 'cli_ignore_unknown_args': False, 'cli_implicit_flags': False, 'cli_kebab_case': False, 'cli_parse_args': None, 'cli_parse_none_str': None, 'cli_prefix': '', 'cli_prog_name': None, 'cli_shortcuts': None, 'cli_use_class_docs_for_groups': False, 'enable_decoding': True, 'env_file': '.env', 'env_file_encoding': None, 'env_ignore_empty': False, 'env_nested_delimiter': None, 'env_nested_max_split': None, 'env_parse_enums': None, 'env_parse_none_str': None, 'env_prefix': '', 'extra': 'allow', 'json_file': None, 'json_file_encoding': None, 'nested_model_default_partial_update': False, 'protected_namespaces': ('model_validate', 'model_dump', 'settings_customise_sources'), 'secrets_dir': None, 'toml_file': None, 'validate_default': True, 'yaml_config_section': None, 'yaml_file': None, 'yaml_file_encoding': None}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

get_config()[source]#

Get the global Axioms configuration instance.

Returns:

The configuration instance.

Return type:

AxiomsConfig

Raises:

RuntimeError – If configuration has not been initialized.

set_config(config)[source]#

Set the global Axioms configuration instance.

Parameters:

config (AxiomsConfig) – The AxiomsConfig instance to use globally.

Return type:

None

init_axioms(app=None, **kwargs)[source]#

Initialize Axioms configuration for a FastAPI application.

Parameters:
  • app – Optional FastAPI application instance.

  • **kwargs – Configuration parameters to override environment variables.

Returns:

The initialized configuration.

Return type:

AxiomsConfig

Example:

from fastapi import FastAPI
from axioms_fastapi import init_axioms

app = FastAPI()
config = init_axioms(
    app,
    AXIOMS_AUDIENCE="api.example.com",
    AXIOMS_DOMAIN="auth.example.com"
)