From 45c59932a693962c4e4861dabd0bba7322e1ce0e Mon Sep 17 00:00:00 2001 From: kitos Date: Fri, 22 May 2026 12:33:03 +0000 Subject: [PATCH] Add wiki page: Authentication-and-Security --- Authentication-and-Security.-.md | 270 +++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) diff --git a/Authentication-and-Security.-.md b/Authentication-and-Security.-.md index e69de29..d0f4303 100644 --- a/Authentication-and-Security.-.md +++ b/Authentication-and-Security.-.md @@ -0,0 +1,270 @@ +# Authentication and Security + +This page covers the full authentication model, token management, API key usage, +SSO configuration, and security controls in Aegis. + +--- + +## Authentication Methods + +Aegis supports two authentication methods, checked in order: + +### 1. HttpOnly Cookie (Primary — Browser Use) + +After a successful login, the server sets: + +``` +Set-Cookie: aegis_token=; HttpOnly; SameSite=Strict; Path=/; [Secure in prod] +``` + +The browser automatically sends this cookie on every request. +**This is the recommended method for the frontend.** + +### 2. Authorization: Bearer Header (API / Machine Use) + +```http +Authorization: Bearer +``` + +Use this for: +- API integrations +- Scripts and CI/CD pipelines +- When cookies are not available (e.g. cross-origin API calls) + +--- + +## JWT Token + +### Structure + +The JWT payload contains: +```json +{ + "sub": "user-uuid", + "role": "red_lead", + "jti": "unique-token-id", + "exp": 1710000000, + "iat": 1709996400 +} +``` + +### Expiry + +Configurable via environment variable: +``` +ACCESS_TOKEN_EXPIRE_MINUTES=480 # 8 hours default +``` + +### Token Revocation (Blacklist) + +On logout, the token's `jti` is stored in Redis with TTL = remaining token lifetime: +``` +Redis key: blacklist:{jti} +Value: "1" +TTL: +``` + +Every authenticated request checks Redis for the `jti`. If found → 401 Unauthorized. + +--- + +## Cookie Security + +| Setting | Development | Production | +|---------|-------------|------------| +| `HttpOnly` | True | True | +| `SameSite` | Strict | Strict | +| `Secure` | False | True | +| `Path` | / | / | + +**`SECURE_COOKIES` environment variable:** +- `auto` (default): `Secure=True` when `AEGIS_ENV=production` +- `true`: Always set `Secure` flag +- `false`: Never set `Secure` flag (development only) + +--- + +## Password Policy + +- Minimum length: **12 characters** +- Must contain: uppercase, lowercase, digit, and special character +- Enforced at account creation and password change +- Passwords are hashed with **bcrypt** (work factor 12) + +### must_change_password + +When an admin creates a user, `must_change_password` is set to `True`. +Until the user changes their password, **every endpoint** returns: +```json +{"detail": "PASSWORD_CHANGE_REQUIRED"} +``` +with status `403`, **except**: +- `GET /api/v1/auth/me` +- `POST /api/v1/auth/change-password` + +Change password: +```http +POST /api/v1/auth/change-password +Content-Type: application/json + +{ + "current_password": "admin-set-password", + "new_password": "MyNewSecurePassword123!" +} +``` + +--- + +## API Keys + +API keys provide long-lived credentials for machine-to-machine access without +session management. + +### Creating a Key + +```http +POST /api/v1/api-keys +Content-Type: application/json + +{ + "name": "CI Pipeline Key", + "scope": "read", + "expires_at": "2025-12-31T00:00:00Z" +} +``` + +Response includes the key value (only shown once): +```json +{ + "id": "uuid", + "name": "CI Pipeline Key", + "key": "aegis_a1b2c3d4e5f6...", + "scope": "read" +} +``` + +### Using a Key + +```http +Authorization: Bearer aegis_a1b2c3d4e5f6... +``` + +### Scopes + +| Scope | Allowed HTTP methods | +|-------|---------------------| +| `read` | GET, HEAD, OPTIONS only | +| `write` | GET, POST, PATCH, PUT, DELETE | +| `admin` | All + admin-only endpoints | + +A `read` scope key that attempts POST/PATCH/DELETE receives: +```json +{"detail": "API key scope 'read' does not permit this operation"} +``` +with status `403`. + +### Revoking a Key + +```http +DELETE /api/v1/api-keys/{id} +``` + +--- + +## SAML 2.0 SSO + +Aegis supports SAML 2.0 for enterprise single sign-on (e.g. Azure AD, Okta, Ping). + +### Configuration (admin only) + +```http +PUT /api/v1/sso/config +Content-Type: application/json + +{ + "enabled": true, + "idp_entity_id": "https://idp.example.com", + "idp_sso_url": "https://idp.example.com/sso/saml", + "idp_certificate": "-----BEGIN CERTIFICATE----- +... +-----END CERTIFICATE-----", + "sp_entity_id": "https://aegis.example.com", + "attribute_mapping": { + "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", + "role": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groups" + } +} +``` + +### SSO Login Flow + +1. Browser visits `GET /api/v1/sso/login` → redirect to IdP +2. User authenticates with IdP +3. IdP POSTs SAML assertion to `POST /api/v1/sso/callback` +4. Aegis validates assertion, finds/creates user, issues JWT cookie +5. Browser redirected to frontend + +### Service Provider Metadata + +```http +GET /api/v1/sso/metadata +``` + +Returns XML metadata to register Aegis as a SAML service provider in your IdP. + +--- + +## Rate Limiting + +- **Login endpoint**: 5 attempts per minute per IP +- Exceeded: `429 Too Many Requests` +- Implemented via Redis counters with TTL + +--- + +## SSRF Protection (Webhooks) + +Webhook URLs are validated before saving. The following IP ranges are blocked: + +| Range | Description | +|-------|-------------| +| 10.0.0.0/8 | Private (Class A) | +| 172.16.0.0/12 | Private (Class B) | +| 192.168.0.0/16 | Private (Class C) | +| 169.254.0.0/16 | Link-local / APIPA | +| 127.0.0.0/8 | Loopback | +| ::1/128 | IPv6 loopback | +| fc00::/7 | IPv6 unique local | +| 0.0.0.0/8 | Reserved | +| 100.64.0.0/10 | Shared address space | + +Attempting to create a webhook pointing to a private IP returns: +```json +{"detail": "Webhook URL points to a private or reserved IP address (SSRF prevention)"} +``` + +--- + +## Audit Logging + +Every significant action is recorded in the `audit_logs` table (admin read-only): + +| Event type | Logged when | +|------------|-------------| +| `auth.login` | Successful login | +| `auth.logout` | Logout | +| `auth.login_failed` | Failed login attempt | +| `auth.password_changed` | Password change | +| `user.created` | New user created | +| `user.updated` | User updated | +| `user.deleted` | User deleted | +| `test.created` | New test created | +| `test.state_changed` | Test state transition | +| `test.validated` | Both leads approved | +| `campaign.completed` | Campaign completed | +| `permission.denied` | 403 on a protected endpoint | + +Query audit logs: +```http +GET /api/v1/audit-logs?user_id=&event_type=auth.login&from=2024-01-01&to=2024-12-31 +```