feat: ASRX hybrid pipeline, identity history, worker fixes, checkpoint system
This commit is contained in:
@@ -0,0 +1,280 @@
|
||||
<!-- module: auth -->
|
||||
<!-- description: Authentication — login, logout, JWT, session cookie, API key -->
|
||||
<!-- depends: -->
|
||||
|
||||
## Base URL
|
||||
|
||||
| Environment | URL | Purpose |
|
||||
|-------------|-----|---------|
|
||||
| Production | `http://localhost:3002` | Production deployment |
|
||||
| External (M5) | `https://m5api.momentry.ddns.net` | Remote access |
|
||||
|
||||
## Variables
|
||||
|
||||
All examples in this documentation use these environment variables:
|
||||
|
||||
```bash
|
||||
API="http://localhost:3002"
|
||||
KEY="your-api-key-here"
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
All endpoints under `/api/v1/*` require authentication.
|
||||
The following endpoints are public (no auth needed):
|
||||
|
||||
- `GET /health`
|
||||
- `POST /api/v1/auth/login`
|
||||
- `POST /api/v1/auth/logout`
|
||||
|
||||
### Three Authentication Modes
|
||||
|
||||
The system supports three authentication methods, checked in **priority order** by the middleware:
|
||||
|
||||
```
|
||||
Middleware priority:
|
||||
1. Session Cookie (Portal/browser)
|
||||
2. JWT Bearer (API clients, CLI)
|
||||
3. API Key Header (legacy compatibility)
|
||||
4. API Key Query Param (?api_key=)
|
||||
```
|
||||
|
||||
| Mode | Transport | Expiry | Scope | Best for |
|
||||
|------|-----------|--------|-------|----------|
|
||||
| **Session Cookie** | `Cookie: session_id=<session_id>` | 24h | per-browser session | Portal (browser) |
|
||||
| **JWT** | `Authorization: Bearer <token>` | 1h | per-login token | API clients, CLI, scripts |
|
||||
| **API Key** | `X-API-Key: <key>` | 90d | fixed key for automation | Legacy scripts, WordPress |
|
||||
|
||||
---
|
||||
|
||||
### Login
|
||||
|
||||
**Default accounts & API keys:**
|
||||
|
||||
| Username | Password | API Key | Role |
|
||||
|----------|----------|---------|------|
|
||||
| `admin` | `admin` | — | admin |
|
||||
| `demo` | `demo` | `muser_demo_key_32chars_abcdef1234567890` | user |
|
||||
|
||||
The demo API key is set via `MOMENTRY_DEMO_API_KEY` env var and can be used in place of JWT for marcom integrations:
|
||||
|
||||
```bash
|
||||
# Using API key instead of JWT
|
||||
curl -s "$API/api/v1/files/scan" -H "X-API-Key: muser_demo_key_32chars_abcdef1234567890"
|
||||
```
|
||||
|
||||
```bash
|
||||
# Login as admin
|
||||
curl -s -X POST "$API/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username": "admin", "password": "admin"}'
|
||||
|
||||
# Login as demo user
|
||||
curl -s -X POST "$API/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username": "demo", "password": "demo"}'
|
||||
```
|
||||
|
||||
#### Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"jwt": "eyJhbGciOiJIUzI1NiIs...",
|
||||
"api_key": "muser_...",
|
||||
"user": {
|
||||
"username": "admin",
|
||||
"role": "admin"
|
||||
},
|
||||
"expires_at": "2026-05-18T13:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `jwt` | string | JWT access token. Use as `Authorization: Bearer <jwt>`. Expires in 1 hour. |
|
||||
| `api_key` | string | Legacy API key. Use as `X-API-Key: <key>`. Good for 90 days. |
|
||||
| `user.username` | string | Username |
|
||||
| `user.role` | string | Role: `admin`, `user`, or `readonly` |
|
||||
| `expires_at` | string | ISO8601 timestamp of JWT expiration |
|
||||
|
||||
The login endpoint also sets a `Set-Cookie` header for browser-based clients:
|
||||
|
||||
```
|
||||
Set-Cookie: session_id=<session_id>; Path=/; HttpOnly; SameSite=Strict; Max-Age=86400
|
||||
```
|
||||
|
||||
#### Error Response (401)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Invalid username or password"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Using JWT
|
||||
|
||||
JWT is preferred for API clients (CLI scripts, WordPress). It is validated by the middleware without a database lookup (stateless).
|
||||
|
||||
```bash
|
||||
# Login and capture JWT
|
||||
JWT=$(curl -s -X POST "$API/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin"}' | python3 -c "import json,sys;print(json.load(sys.stdin)['jwt'])")
|
||||
|
||||
# Use JWT for all subsequent requests
|
||||
curl -H "Authorization: Bearer $JWT" "$API/api/v1/files/scan"
|
||||
curl -H "Authorization: Bearer $JWT" "$API/api/v1/resource/tmdb"
|
||||
```
|
||||
|
||||
JWT is short-lived (1 hour). When it expires, request a new one via login.
|
||||
|
||||
---
|
||||
|
||||
### Using Session Cookie (Browser)
|
||||
|
||||
Browser-based clients (Portal) get a session cookie automatically after login. The browser sends the cookie with every request—no manual header needed.
|
||||
|
||||
```bash
|
||||
# Login captures the session cookie from Set-Cookie header
|
||||
curl -v -X POST "$API/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin"}' 2>&1 | grep "Set-Cookie"
|
||||
|
||||
# Browser automatically sends: Cookie: session_id=<session_id>
|
||||
# No manual header needed for subsequent requests
|
||||
```
|
||||
|
||||
The session cookie is HttpOnly (not accessible from JavaScript) and SameSite=Strict (protected against CSRF).
|
||||
|
||||
---
|
||||
|
||||
### Using Legacy API Key
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: $KEY" "$API/api/v1/files/scan"
|
||||
|
||||
# Also accepted via Bearer header (non-JWT format) or query parameter:
|
||||
curl -H "Authorization: Bearer $KEY" "$API/api/v1/files/scan"
|
||||
curl "$API/api/v1/files/scan?api_key=$KEY"
|
||||
```
|
||||
|
||||
API keys are validated via SHA256 hash lookup in the database. They are long-lived (90 days) and intended for automation.
|
||||
|
||||
### Obtaining an API Key (CLI)
|
||||
|
||||
```bash
|
||||
momentry api-key create "My API Key" --key-type user
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Logout
|
||||
|
||||
```bash
|
||||
# Logout using the session cookie (browser)
|
||||
curl -X POST "$API/api/v1/auth/logout" \
|
||||
-H "Cookie: session_id=<uuid>"
|
||||
```
|
||||
|
||||
#### What logout does
|
||||
|
||||
| Auth mode | Effect |
|
||||
|-----------|--------|
|
||||
| **Session Cookie** | Session deleted from database. Same cookie returns 401 on subsequent requests. |
|
||||
| **JWT** | JWT remains valid until expiry. (JWT is stateless — logout adds JWT to a blacklist only if API key mode is used.) |
|
||||
| **API Key** | API key remains valid. (Legacy keys are shared across sessions — revoking would break other clients.) |
|
||||
|
||||
#### Example: full session lifecycle
|
||||
|
||||
```bash
|
||||
# 1. Login
|
||||
SESSION_ID=$(curl -s -D - -X POST "$API/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin"}' | grep "Set-Cookie" | sed 's/.*session_id=\([^;]*\).*/\1/')
|
||||
|
||||
# 2. Use session (works)
|
||||
curl -s -o /dev/null -w "HTTP %{http_code}\n" "$API/api/v1/resource/tmdb" \
|
||||
-H "Cookie: session_id=$SESSION_ID"
|
||||
# → HTTP 200
|
||||
|
||||
# 3. Logout
|
||||
curl -s -X POST "$API/api/v1/auth/logout" \
|
||||
-H "Cookie: session_id=$SESSION_ID"
|
||||
# → {"success": true}
|
||||
|
||||
# 4. Use session again (rejected)
|
||||
curl -s -o /dev/null -w "HTTP %{http_code}\n" "$API/api/v1/resource/tmdb" \
|
||||
-H "Cookie: session_id=$SESSION_ID"
|
||||
# → HTTP 401
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Authentication Flow Summary
|
||||
|
||||
```
|
||||
Login Request
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ 1. Check users │ ← users table (argon2 password verify)
|
||||
│ table │
|
||||
└──────┬───────────┘
|
||||
│
|
||||
┌───┴───┐
|
||||
│ match │
|
||||
└───┬───┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ 2. Create JWT │ ← 1h expiry, signed with JWT_SECRET
|
||||
├──────────────────┤
|
||||
│ 3. Create │ ← 24h expiry, stored in sessions table
|
||||
│ session │
|
||||
├──────────────────┤
|
||||
│ 4. Set-Cookie │ ← HttpOnly, SameSite=Strict, Path=/
|
||||
├──────────────────┤
|
||||
│ 5. Return │ ← JWT + api_key + user info to client
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
```
|
||||
Protected Request
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ Middleware checks: │
|
||||
│ │
|
||||
│ 1. Cookie session? │ → DB lookup session → get api_key → verify
|
||||
│ │
|
||||
│ 2. JWT Bearer? │ → verify JWT signature → decode claims
|
||||
│ │
|
||||
│ 3. X-API-Key? │ → SHA256 hash → DB lookup → verify
|
||||
│ │
|
||||
│ 4. ?api_key=? │ → same as #3
|
||||
│ │
|
||||
│ 5. None → 401 │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Error Responses
|
||||
|
||||
| HTTP | When |
|
||||
|------|------|
|
||||
| `401` | Missing or invalid authentication |
|
||||
| `401` | Session expired or logged out |
|
||||
| `401` | JWT expired |
|
||||
| `401` | API key revoked or inactive |
|
||||
|
||||
---
|
||||
|
||||
### Related
|
||||
|
||||
- `POST /api/v1/resource/tmdb/check` — test authentication + TMDb API connectivity
|
||||
- `GET /health/detailed` — view auth status (integrations section)
|
||||
Reference in New Issue
Block a user