Source code for axioms_core.config

"""Axioms configuration management."""

# ruff: noqa: N803
# Allow uppercase argument names for config (they match environment variable names)

import logging
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)


[docs] class AxiomsConfig: """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()) """
[docs] def __init__( self, # Auth settings AXIOMS_DOMAIN: Optional[str] = None, AXIOMS_ISS_URL: Optional[str] = None, AXIOMS_AUDIENCE: Optional[str] = None, AXIOMS_JWKS_URL: Optional[str] = None, AXIOMS_TOKEN_TYPS: Optional[List[str]] = None, AXIOMS_SAFE_METHODS: Optional[List[str]] = None, # JWKS Manager settings AXIOMS_JWKS_REFRESH_INTERVAL: int = 3600, # 1 hour default AXIOMS_JWKS_CACHE_TTL: int = 7200, # 2 hours default (2x refresh interval) AXIOMS_JWKS_PREFETCH: bool = True, # Claim name customization (lists of claim names to check in priority order) AXIOMS_SCOPE_CLAIMS: Optional[List[str]] = None, AXIOMS_ROLES_CLAIMS: Optional[List[str]] = None, AXIOMS_PERMISSIONS_CLAIMS: Optional[List[str]] = None, ): """Initialize Axioms configuration. Args: AXIOMS_DOMAIN: Auth provider domain (e.g., "auth.example.com") AXIOMS_ISS_URL: Full issuer URL (overrides domain) AXIOMS_AUDIENCE: Expected audience for token validation AXIOMS_JWKS_URL: JWKS endpoint URL (overrides auto-discovery) AXIOMS_TOKEN_TYPS: Allowed token types (default: ["JWT", "jwt", "at+jwt", "application/jwt"]) AXIOMS_SAFE_METHODS: HTTP methods that bypass authorization checks (default: ["OPTIONS"] - commonly used for CORS preflight) AXIOMS_JWKS_REFRESH_INTERVAL: Seconds between JWKS refreshes (default: 3600) AXIOMS_JWKS_CACHE_TTL: Seconds before cache entry expires (default: 7200, must be >= 2x refresh interval) AXIOMS_JWKS_PREFETCH: Whether to fetch JWKS on initialization (default: True) AXIOMS_SCOPE_CLAIMS: List of claim names to check for scopes (default: ["scope"]) AXIOMS_ROLES_CLAIMS: List of claim names to check for roles (default: ["roles"]) AXIOMS_PERMISSIONS_CLAIMS: List of claim names to check for permissions (default: ["permissions"]) """ # Auth settings self.AXIOMS_DOMAIN = AXIOMS_DOMAIN self.AXIOMS_ISS_URL = AXIOMS_ISS_URL self.AXIOMS_AUDIENCE = AXIOMS_AUDIENCE self.AXIOMS_JWKS_URL = AXIOMS_JWKS_URL self.AXIOMS_TOKEN_TYPS = AXIOMS_TOKEN_TYPS or [ "JWT", "jwt", "at+jwt", "application/jwt", ] self.AXIOMS_SAFE_METHODS = AXIOMS_SAFE_METHODS or ["OPTIONS"] # JWKS Manager settings self.AXIOMS_JWKS_REFRESH_INTERVAL = AXIOMS_JWKS_REFRESH_INTERVAL self.AXIOMS_JWKS_CACHE_TTL = AXIOMS_JWKS_CACHE_TTL self.AXIOMS_JWKS_PREFETCH = AXIOMS_JWKS_PREFETCH # Claim name customization (lists for checking multiple claim names in priority order) self.AXIOMS_SCOPE_CLAIMS = AXIOMS_SCOPE_CLAIMS or ["scope"] self.AXIOMS_ROLES_CLAIMS = AXIOMS_ROLES_CLAIMS or ["roles"] self.AXIOMS_PERMISSIONS_CLAIMS = AXIOMS_PERMISSIONS_CLAIMS or ["permissions"] # Validate configuration self._validate()
def _validate(self): """Validate configuration values.""" if self.AXIOMS_JWKS_REFRESH_INTERVAL <= 0: raise ValueError("AXIOMS_JWKS_REFRESH_INTERVAL must be positive") if self.AXIOMS_JWKS_CACHE_TTL <= 0: raise ValueError("AXIOMS_JWKS_CACHE_TTL must be positive") # Cache TTL must be at least 2x refresh interval to prevent cache expiry before refresh min_cache_ttl = 2 * self.AXIOMS_JWKS_REFRESH_INTERVAL if self.AXIOMS_JWKS_CACHE_TTL < min_cache_ttl: raise ValueError( f"AXIOMS_JWKS_CACHE_TTL ({self.AXIOMS_JWKS_CACHE_TTL}) must be at least " f"2x AXIOMS_JWKS_REFRESH_INTERVAL " f"(2 * {self.AXIOMS_JWKS_REFRESH_INTERVAL} = {min_cache_ttl}). " f"This prevents cache expiry before the next refresh cycle." )
[docs] def to_dict(self) -> Dict[str, Any]: """Convert configuration to dictionary. Returns: Dictionary containing all configuration values. """ return { # Auth settings "AXIOMS_DOMAIN": self.AXIOMS_DOMAIN, "AXIOMS_ISS_URL": self.AXIOMS_ISS_URL, "AXIOMS_AUDIENCE": self.AXIOMS_AUDIENCE, "AXIOMS_JWKS_URL": self.AXIOMS_JWKS_URL, "AXIOMS_TOKEN_TYPS": self.AXIOMS_TOKEN_TYPS, "AXIOMS_SAFE_METHODS": self.AXIOMS_SAFE_METHODS, # JWKS Manager settings "AXIOMS_JWKS_REFRESH_INTERVAL": self.AXIOMS_JWKS_REFRESH_INTERVAL, "AXIOMS_JWKS_CACHE_TTL": self.AXIOMS_JWKS_CACHE_TTL, "AXIOMS_JWKS_PREFETCH": self.AXIOMS_JWKS_PREFETCH, # Claim name customization "AXIOMS_SCOPE_CLAIMS": self.AXIOMS_SCOPE_CLAIMS, "AXIOMS_ROLES_CLAIMS": self.AXIOMS_ROLES_CLAIMS, "AXIOMS_PERMISSIONS_CLAIMS": self.AXIOMS_PERMISSIONS_CLAIMS, }
[docs] def __repr__(self) -> str: """String representation of config.""" items = ", ".join(f"{k}={v!r}" for k, v in self.to_dict().items()) return f"AxiomsConfig({items})"
[docs] def get_config_value(config: Optional[Any], key: str, default: Any = None) -> Any: """Get configuration value from config object. Supports both dict-like and object attribute access patterns. Args: config: Configuration object or dict. key: Configuration key to retrieve. default: Default value if key not found. Returns: Configuration value or default. """ if config is None: return default # Try dict-like access first if isinstance(config, dict): return config.get(key, default) # Try attribute access (for objects) return getattr(config, key, default)