## 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=` | 24h | per-browser session | Portal (browser) | | **JWT** | `Authorization: Bearer ` | 1h | per-login token | API clients, CLI, scripts | | **API Key** | `X-API-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 `. Expires in 1 hour. | | `api_key` | string | Legacy API key. Use as `X-API-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=; 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= # 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=" ``` #### 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)