# Identity and Authentication ## Purpose Document how user identity, JWT handling, API keys, and session flows work, including middleware and validation rules. ## Code Locations (exact paths) - JWT utilities: `backend/igny8_core/auth/utils.py` - Account context middleware: `backend/igny8_core/auth/middleware.py` - DRF authentication classes: `backend/igny8_core/api/authentication.py` - DRF settings for auth/throttle: `backend/igny8_core/settings.py` - User model and roles: `backend/igny8_core/auth/models.py` - Auth URLs and views: `backend/igny8_core/auth/urls.py`, `backend/igny8_core/auth/views.py` ## High-Level Responsibilities - Support multiple auth mechanisms: API key (WordPress bridge), JWT bearer tokens, session auth without CSRF for APIs, and basic auth fallback. - Populate tenant/site context alongside user identity so downstream viewsets enforce isolation. - Enforce active account/plan presence before serving protected endpoints (except admin/auth routes). ## Detailed Behavior - Authentication order (DRF `DEFAULT_AUTHENTICATION_CLASSES` in `settings.py`): API key → JWT → CSRF-exempt session → basic auth. The first class that authenticates sets `request.user`; `request.account` may also be set by API key or JWT. - API Key flow (`APIKeyAuthentication`): expects `Authorization: Bearer ` that is not JWT-like; finds an active `Site` with `wp_api_key`, loads its `account`, and selects an active user (owner preferred, else any active developer/owner/admin). Sets `request.account` and `request.site`. Rejects short/invalid keys; returns an auth failure if no active user exists. - JWT flow (`JWTAuthentication`): expects `Authorization: Bearer `; decodes via `auth.utils.decode_token`; only accepts tokens with `type == access`. Retrieves `User` by `user_id`; optional `account_id` is resolved to `Account` and set on `request.account`. Invalid/expired tokens fall through to other auth classes. - Session flow (`CSRFExemptSessionAuthentication`): uses Django session cookies without CSRF enforcement for API calls. - Basic auth: last resort; does not set tenant context. - Token utilities (`auth/utils.py`) generate and decode access/refresh tokens using expiries from settings (`JWT_ACCESS_TOKEN_EXPIRY`, `JWT_REFRESH_TOKEN_EXPIRY`), embedding `user_id`, `account_id`, `email`, issued/expiry timestamps, and token `type`. - Middleware (`AccountContextMiddleware`) runs on every request except admin/auth paths: refreshes session users from DB to pick up current account/plan, validates presence of an active plan, sets `request.account`, and logs out session users when invalid. For JWT-bearing requests it decodes the token directly and sets `request.account`. If account/plan is missing or inactive, it returns JSON with `success=false` and appropriate HTTP status. ## Data Structures / Models Involved (no code) - `User` with `role` and `account` FKs in `auth/models.py`. - `Account` with plan and billing fields; plan status is used for access gating. - `Site` with `wp_api_key` for API key auth; `SiteUserAccess` for per-site grants. - `PasswordResetToken` model for password reset flows. - JWT payload fields: `user_id`, `account_id`, `email`, `exp`, `iat`, `type`. ## Execution Flow - Middleware step: `AccountContextMiddleware` determines `request.account` (session or JWT) and validates plan status; skips admin/auth routes. - DRF auth step: API key/JWT/session/basic authenticators run in order, potentially setting `request.account` (API key/JWT) and `request.site` (API key). - Viewsets then apply role/permission checks and tenant/site/sector filtering via base classes in `api/base.py`. ## Cross-Module Interactions - All module viewsets rely on `request.user` and `request.account` set by the auth stack. Site-aware modules can read `request.site` when API key auth is used. - Role helpers (`is_admin_or_developer`, `is_system_account_user`) influence filtering bypass in base viewsets. ## State Transitions (if applicable) - JWT lifetimes: access tokens default to 15 minutes; refresh tokens to 30 days (configurable in settings). - Session users are refreshed on each request to pick up plan/account changes. - Password reset tokens track expiry and usage via `expires_at` and `used` flags. ## Error Handling - Middleware returns JSON errors for missing account or inactive plan and logs out session users in those cases. - Invalid/expired JWTs cause the JWT authenticator to return `None`, allowing other auth methods; decoding errors raise `InvalidTokenError` in utilities. - API key auth raises an auth failure when no active user is available for the resolved account. ## Tenancy Rules - `request.account` is set early; base viewsets enforce account filtering unless user has admin/developer/system-account privileges. - API key auth also sets `request.site` for integration contexts; site/sector filtering occurs in `SiteSectorModelViewSet`. ## Billing Rules (if applicable) - Active plan is required for access (middleware enforces). Credit debits/charges are handled in billing modules, not in the auth layer. ## Background Tasks / Schedulers (if applicable) - Token generation/validation is synchronous. Background tasks should receive explicit user/account identifiers in their payloads when invoked. ## Key Design Considerations - Authentication stack is ordered to give integration API keys precedence, then JWT for app clients, then session for browser-based flows. - Tenant context must be established before view logic; do not move or remove `AccountContextMiddleware`. - Expiry durations and JWT secrets are centrally configured in `settings.py`. ## How Developers Should Work With This Module - Use token helpers from `auth/utils.py` when issuing tokens; do not handcraft JWTs. - Mount new auth-sensitive endpoints under existing routers and rely on DRF auth classes instead of custom header parsing. - Ensure new features that require site context can work with API key auth by checking `request.site`. - Keep plan enforcement in place; bypass only for admin/system routes when justified.