gh_UserManager/docs/authentication.md
bermooda-company 54d5891edf user
2026-08-23 23:59:14 +03:30

283 lines
8.2 KiB
Markdown

# 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 <access_token>" \
-H "Content-Type: application/json" \
-d '{"refresh":"<refresh_token>"}'
```
## 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 <admin_token>" \
-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 <access_token>
# Revoke session (current or other)
DELETE /api/v1/sessions/{session_id}
Authorization: Bearer <access_token>
```
**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