Skip to content

Identity and Access Management (IAM)

The shared.iam package provides a unified system for authentication and authorization across all Alan applications.

Overview

The IAM system is built around three core concepts:

  1. Authentication Context - Tracks who is making a request (the "principal") and on whose behalf
  2. Auth Context Providers - Extract authentication information from incoming requests
  3. Access Policies (ABAC) - Define and enforce fine-grained access control rules
flowchart LR
    A[Request] --> B[AuthContextProvider]
    B --> C[AuthContext]
    C --> D[AccessPolicy]

    B -.- B1["Extract auth from headers"]
    C -.- C1["Store principal info"]
    D -.- D1["Enforce access rules"]
Hold "Alt" / "Option" to enable pan & zoom

Key Components

AuthPrincipal

A principal is any entity that can be authenticated - members, alaners, external contractors, service accountsโ€ฆ All authenticatable models must implement the AuthPrincipal mixin:

from shared.iam.auth_principal import AuthPrincipal, AuthPrincipalType
from shared.models.orm.base import BaseModel


class User(AuthPrincipal, BaseModel):
    __auth_principal_type__ = AuthPrincipalType.front_office
    __identifier_column__ = "email"  # Column used for lookups

    @property
    def is_active(self) -> bool:
        return not self.deleted

    @property
    def principal_name(self) -> str:
        return self.full_name

Principal Types:

  • front_office - End users (members, company admins)
  • back_office - principals with an AlanEmployee entry (Alaners, contractors, service accounts)

AuthContext

The AuthContext is a frozen dataclass that stores authentication state for each request. It tracks:

  • Real principal - The actual actor performing the action
  • Effective principal - The subject of the action (same as real, or an impersonated user)
  • Delegate principal - Optional principal acting on behalf of the real principal
  • Session info - Session ID, scopes, and metadata

Never create AuthContext manually

Always access the auth context via current_auth_context. The context is managed by auth providers and must never be instantiated directly. Creating an AuthContext manually bypasses security controls and breaks audit trails.

Access it via current_auth_context:

from shared.iam.helpers import current_auth_context

# Check authentication status
if current_auth_context.is_authenticated:
    user = current_auth_context.real_principal_as(User)
    print(f"Request by {user.email}")

# Check for impersonation
if current_auth_context.is_impersonated:
    actor = current_auth_context.real_principal  # Alaner or ExternalUser
    subject = current_auth_context.effective_principal  # Impersonated user

See Authentication Context for detailed documentation.

Auth Context Providers

Providers extract authentication information from incoming requests (JWT tokens, cookies, headers) and set up the AuthContext. They're evaluated in a chain until exactly one matches.

Default setup: The default provider chain is configured at app initialization via CustomApi.register_default_auth_context_providers(). Most blueprints inherit this default.

Blueprint customization: Individual blueprints can override the chain:

  • In the CustomBlueprint constructor via auth_context_providers parameter
  • During blueprint registration
from shared.api.custom_smorest_blueprint import CustomBlueprint
from shared.iam.auth_context.providers.zero_trust import ZeroTrustAuthContextProvider
from shared.iam.auth_context.providers.anonymous import AnonymousAuthContextProvider
from apps.eu_tools.alan_home.models.alaner import Alaner

# Blueprint with custom auth providers
public_blueprint = CustomBlueprint(
    "public",
    __name__,
    auth_context_providers=[
        ZeroTrustAuthContextProvider(Alaner),
        AnonymousAuthContextProvider(),  # Allow unauthenticated access
    ],
)

See Authentication Context for provider documentation.

Access Policies (ABAC)

Attribute-Based Access Control policies define who can do what. Policies are generally enforced using the @enforce_policy decorator:

from shared.iam.abac.access_policy import AccessPolicy, enforce_policy


class DocumentOwnerPolicy(AccessPolicy):
    """Only document owners can access their documents"""
    policy_id = "document-owner"

    @classmethod
    def evaluate(cls, document_id: int) -> bool:
        doc = get_document(document_id)
        return doc.owner_id == current_auth_context.effective_principal.id


@enforce_policy(DocumentOwnerPolicy)
def get_document(document_id: int) -> Document:
    return get_or_404(Document, document_id)

See Access Control for detailed ABAC documentation.

Quick Reference

Component Purpose Key Import
current_auth_context Access current request's auth info from shared.iam.helpers import current_auth_context
set_auth_context Set auth context (for providers) from shared.iam.helpers import set_auth_context
AuthContextProvider Base class for auth extraction from shared.iam.auth_context.providers.base import AuthContextProvider
AccessPolicy Base class for access rules from shared.iam.abac.access_policy import AccessPolicy
enforce_policy Decorator to enforce policies from shared.iam.abac.access_policy import enforce_policy

Reference