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:
- Authentication Context - Tracks who is making a request (the "principal") and on whose behalf
- Auth Context Providers - Extract authentication information from incoming requests
- 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"]
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 anAlanEmployeeentry (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
CustomBlueprintconstructor viaauth_context_providersparameter - 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¶
- Authentication Context - AuthContext, providers, impersonation
- Access Control - AccessPolicy, enforce_policy, built-in policies