# Authentication ## Overview Identity Platform implements **OAuth 2.0 + OpenID Connect** with **RS256 asymmetric signing**. The platform is the Authorization Server; products are Resource Servers / Relying Parties. ## Token Flow (Authorization Code + PKCE) ``` ┌──────────┐ ┌──────────────────┐ │ Product │ │ Identity Platform │ └────┬─────┘ └────────┬─────────┘ │ │ │ 1. GET /.well-known/openid-configuration │ │◄────────────────────────────────────────────│ │ │ │ 2. Redirect user to /authorize │ │────────────────────────────────────────────►│ │ │ │ 3. User authenticates (login/MFA) │ │ │ │ 4. User consents to scopes │ │ │ │ 5. Redirect back with ?code=... │ │◄────────────────────────────────────────────│ │ │ │ 6. POST /api/v1/auth/token (code + verifier)│ │────────────────────────────────────────────►│ │ │ │ 7. { access_token, refresh_token, id_token }│ │◄────────────────────────────────────────────│ │ │ │ 8. Product verifies access_token via JWKS │ │ │ │ 9. API calls with Authorization: Bearer ... │ ``` ## Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/.well-known/openid-configuration` | GET | OIDC Discovery | | `/.well-known/jwks.json` | GET | JWKS (public keys) | | `/api/v1/auth/login` | POST | Username/password → tokens | | `/api/v1/auth/refresh` | POST | Refresh token → new access | | `/api/v1/auth/logout` | POST | Revoke session + refresh | | `/api/v1/users/me` | GET | Current user info | ### Login Request ```bash curl -X POST https://id.example.com/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"user@acme.com","password":"••••••••"}' ``` **Response:** ```json { "access": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer", "expires_in": 900 } ``` ### Refresh Request ```bash curl -X POST https://id.example.com/api/v1/auth/refresh \ -H "Content-Type: application/json" \ -d '{"refresh":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."}' ``` ### Logout Request ```bash curl -X POST https://id.example.com/api/v1/auth/logout \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"refresh":""}' ``` ## Token Details ### Access Token (JWT, RS256) ```json { "user_id": "uuid", "email": "user@acme.com", "org_id": "uuid", "scopes": ["openid", "profile", "org:read"], "session_id": "uuid", "token_type": "access", "exp": 1700000000, "iat": 1699999100, "iss": "https://id.example.com", "aud": "bermooda-client-id" } ``` - **Lifetime:** 15 minutes (configurable) - **Signed by:** Platform private key - **Verified by:** Product via JWKS ### Refresh Token (JWT, RS256) ```json { "user_id": "uuid", "session_id": "uuid", "token_type": "refresh", "exp": 1702591100, "iat": 1699999100 } ``` - **Lifetime:** 30 days (configurable) - **Stored hashed** in DB (not reversible) - **Rotation:** New refresh token issued on each use - **Revocable:** Instantly via `/auth/logout` or session revocation ### ID Token (OIDC) ```json { "sub": "uuid", "email": "user@acme.com", "email_verified": true, "name": "User Name", "preferred_username": "user", "org_id": "uuid", "iat": 1699999100, "exp": 1700000000, "iss": "https://id.example.com", "aud": "bermooda-client-id" } ``` ## JWKS (JSON Web Key Set) ```bash curl https://id.example.com/.well-known/jwks.json ``` ```json { "keys": [ { "kty": "RSA", "use": "sig", "kid": "identity-platform-1", "alg": "RS256", "n": "...", "e": "AQAB" } ] } ``` Products **cache JWKS** and rotate keys automatically. ## Product Integration ### 1. Register Application ```bash curl -X POST https://id.example.com/api/v1/applications \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "Bermooda", "redirect_uris": ["https://bermooda.example.com/callback"], "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "scopes": ["openid", "profile", "email", "org:read"] }' ``` **Response:** `client_id`, `client_secret` (shown once). ### 2. Configure Product ```env # Product .env OIDC_ISSUER=https://id.example.com OIDC_CLIENT_ID=bermooda-client-id OIDC_CLIENT_SECRET=•••••••• OIDC_REDIRECT_URI=https://bermooda.example.com/callback OIDC_SCOPES=openid profile email org:read ``` ### 3. Verify Tokens (Middleware Example) ```python # Python/DRF example from rest_framework_simplejwt.authentication import JWTAuthentication from rest_framework_simplejwt.backends import TokenBackend token_backend = TokenBackend( algorithm="RS256", signing_key=None, # uses JWKS jwks_url="https://id.example.com/.well-known/jwks.json", audience="bermooda-client-id", issuer="https://id.example.com", ) def verify_token(token: str) -> dict: return token_backend.decode(token, verify=True) ``` ## Security Features | Feature | Implementation | |---------|----------------| | **RS256 Signing** | Private key in platform only | | **PKCE** | Required for public clients | | **Token Rotation** | New refresh token on each refresh | | **Session Revocation** | Instant, cascades to all tokens | | **Brute-force Protection** | Rate limit + auto-lockout (5 failed → 15min) | | **Audit Log** | Every auth event recorded | | **Device Tracking** | Fingerprint + IP + User-Agent | ## MFA (Roadmap) - TOTP (Google Authenticator, Authy) - WebAuthn / Passkeys (future) - Recovery codes - Per-user enrollment ## Session Management ```bash # List active sessions GET /api/v1/sessions Authorization: Bearer # Revoke session (current or other) DELETE /api/v1/sessions/{session_id} Authorization: Bearer ``` **Session object:** ```json { "id": "uuid", "user": "uuid", "device_name": "Chrome on macOS", "ip_address": "192.168.1.1", "user_agent": "Mozilla/5.0...", "created_at": "2024-01-15T10:30:00Z", "last_activity": "2024-01-15T10:45:00Z", "status": "active", "type": "browser" } ``` ## Rate Limits | Endpoint | Limit | |----------|-------| | `/auth/login` | 5/min per IP, 10/min per user | | `/auth/refresh` | 20/min per user | | `/auth/logout` | 10/min per user | | API (general) | 100/min per user | Exceeding → `429 Too Many Requests` with `Retry-After` header. ## Error Responses ```json { "error": "invalid_grant", "error_description": "Invalid or expired refresh token", "error_code": "TOKEN_EXPIRED" } ``` Common OIDC errors: `invalid_request`, `invalid_client`, `invalid_grant`, `unauthorized_client`, `unsupported_grant_type`, `invalid_scope`. ## Testing Checklist - [ ] Login returns access + refresh + id_token - [ ] Access token verified via JWKS - [ ] Refresh rotates token, old refresh revoked - [ ] Logout revokes session + refresh token - [ ] Expired access token → 401 - [ ] Revoked session → 401 on API calls - [ ] Rate limit triggers 429 - [ ] Failed login recorded in audit log - [ ] PKCE required for public clients