API authentication
Cinephage uses Better Auth for authentication with session-based login and API key support.
Authentication Methods
| Method | Use Case | Auth Type |
|---|---|---|
| Session | Web UI access | Cookie-based |
| Main API Key | Full API access, automation | Header/Query |
| Streaming API Key | Media server integration | Header/Query |
Session Authentication
Login Flow
- POST to
/api/auth/sign-in/credentialwith username and password - Server creates session and sets cookie
- Subsequent requests include session cookie automatically
Session Properties
| Property | Value |
|---|---|
| Duration | 7 days |
| Refresh | Every 24 hours |
| Cookie Name | cinephage_session |
| HttpOnly | Yes |
| SameSite | Lax |
Single Admin Architecture
Cinephage uses a single-administrator model:
- First user registration becomes the admin
- Additional registrations are disabled after setup
- Only one user account exists per installation
API Key Authentication
API Key Types
| Key Type | Permissions | Use Case |
|---|---|---|
| Main API Key | Full access to all endpoints | Automation, scripts, tools |
| Streaming API Key | Live TV, EPG, streaming only | Media server integration |
Streaming API Key Permissions
| Allowed | Denied |
|---|---|
/api/livetv/* | /api/library/* |
/api/streaming/* | /api/settings/* |
/api/livetv/playlist.m3u | /api/indexers/* |
/api/livetv/epg.xml | Admin-only operations |
Using API Keys
Via Header (Recommended):
curl -H "x-api-key: cinephage_your_key_here" \
http://localhost:3000/api/library/movies
Via Query Parameter:
curl "http://localhost:3000/api/livetv/playlist.m3u?api_key=cinephage_your_key_here"
When to Use Query Parameter
Some media servers (Plex, Jellyfin) can't send custom headers. Use the query parameter for M3U playlist URLs.
Managing API Keys
Navigate to Settings > System:
| Action | Description |
|---|---|
| View | Show full key value |
| Copy | Copy to clipboard |
| Regenerate | Create new key (old key invalidates) |
Regenerating Keys
Regenerating a key immediately invalidates the old key. Update any services using the key before regenerating.
API Key Storage
API keys are encrypted at rest:
- Algorithm: AES-256-GCM
- Encryption key derived from
BETTER_AUTH_SECRET - Stored in
userApiKeySecretsdatabase table
Rate Limiting
API Key Rate Limits
| Key Type | Window | Max Requests |
|---|---|---|
| Streaming API | 1 hour | 10,000 |
Configure via environment variables:
| Variable | Default | Description |
|---|---|---|
STREAMING_API_KEY_RATE_LIMIT_WINDOW_MS | 3600000 | Window in ms (1 hr) |
STREAMING_API_KEY_RATE_LIMIT_MAX | 10000 | Max requests/window |
Login Rate Limiting
| Limit | Value |
|---|---|
| Max Attempts | 5 |
| Window | 15 minutes |
Endpoint Authorization
Authorization Levels
| Level | Function | Description |
|---|---|---|
| None | - | Public endpoint |
| requireAuth | requireAuth() | Any authenticated user |
| requireAdmin | requireAdmin() | Admin role required |
Response Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 401 | Not authenticated |
| 403 | Authenticated but not authorized |
Common Endpoint Patterns
Public (No Auth):
/api/health/api/ready- Most GET endpoints
Authenticated (requireAuth):
/api/library/*(read operations)/api/search
Admin Only (requireAdmin):
/api/settings/*/api/indexers/*(create/update/delete)/api/download-clients/*(create/update/delete)
Environment Variables
Required
| Variable | Description |
|---|---|
BETTER_AUTH_SECRET | Secret for session signing and encryption |
Optional
| Variable | Default | Description |
|---|---|---|
BETTER_AUTH_URL | From settings | Base URL for auth callbacks |
BETTER_AUTH_TRUSTED_ORIGINS | None | Additional trusted origins |
Generating BETTER_AUTH_SECRET
# Using openssl
openssl rand -base64 32
# Using Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Changing BETTER_AUTH_SECRET
Changing this secret will:
- Invalidate all active sessions (users must re-login)
- Make encrypted API keys unreadable (must regenerate)
- Cannot be undone
Trusted Origins
Cinephage automatically trusts:
- Local development:
localhost:3000,localhost:5173,127.0.0.1 - Private networks: RFC1918 addresses (10.x, 172.16-31.x, 192.168.x)
- Environment configured:
BETTER_AUTH_URL,ORIGIN - Settings configured: External URL from Settings > System
Security Best Practices
API Key Security
- Don't commit keys to version control
- Use environment variables in scripts
- Use Streaming API Key for media servers (least privilege)
- Regenerate compromised keys immediately
BETTER_AUTH_SECRET
- Generate a unique value for each installation
- Back it up securely — needed for database restoration
- Don't change it unless absolutely necessary
- Treat it like a password — keep it secret
Network Security
- Use HTTPS in production
- Restrict admin endpoints with reverse proxy rules
- Use VPN for remote access when possible
Troubleshooting
"401 Unauthorized"
Session auth:
- Session may have expired — log in again
- Cookie may be blocked — check browser settings
API key auth:
- Key may be invalid — verify in Settings > System
- Key may be wrong type — use Main API Key for library access
"403 Forbidden"
- Authenticated but not admin
- Check if endpoint requires admin role
- Streaming API Key used for non-streaming endpoint
API Key Not Working
- Check key is correct — Copy from Settings > System
- Check header name — Must be
x-api-key - Check permissions — Streaming key has limited access
- Check rate limits — May be temporarily blocked
Sessions Invalid After Restart
If all sessions are invalidated after restart:
BETTER_AUTH_SECRETmay have changed- Database may have been reset
- This is expected behavior with new secret
See Also
- Endpoints Overview — Available API endpoints
- Environment Variables — Configuration reference
- Settings Explained — UI configuration