Files
igny8/docs/00-SYSTEM/AUTHENTICATION.md
IGNY8 VPS (Salman) 6a4f95c35a docs re-org
2025-12-09 13:26:35 +00:00

74 lines
5.9 KiB
Markdown

# 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 <api_key>` 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 <jwt>`; 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.