74 lines
5.9 KiB
Markdown
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.
|