diff --git a/.env.example b/.env.example index 8706ca3..b220868 100644 --- a/.env.example +++ b/.env.example @@ -22,5 +22,17 @@ SESSION_SECRET=changeme # MAM API Configuration MAM_API_URL=http://mam-api:3000 -# Auth (set to 'true' to require login; false for open/dev mode) -AUTH_ENABLED=false +# Auth — default to ON in production. Setting to 'false' is a dev-only escape +# hatch that disables all auth checks and attaches a synthetic 'dev' user to +# every request. Never run with AUTH_ENABLED=false on a network you don't control. +AUTH_ENABLED=true + +# CORS allowlist — comma-separated origins that may carry credentials to the API. +# Same-origin requests via the nginx reverse proxy do not need to be listed here. +# Leave empty to allow any origin (DEV ONLY). +ALLOWED_ORIGINS= + +# Reverse-proxy trust — set 'true' when the API sits behind nginx terminating HTTPS, +# so secure-cookie + X-Forwarded-Proto behave correctly. ALSO required for accurate +# per-IP login rate-limiting (otherwise req.ip is always the nginx IP). +TRUST_PROXY=false diff --git a/README.md b/README.md index e7a27f6..a7d4a06 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,54 @@ Total time from end of capture to relinked master: ~2 minutes. - `deploy/test-cluster.sh` — primary↔worker connectivity smoke test - `docs/GROWING_FILES_QUICKSTART.md` — Premiere CEP panel install + growing-file flow +## Authentication + +Dragonflight uses local username/password authentication with two transports: + +- **Browser:** session cookie (`dragonflight.sid`), 8 hour absolute + 1 hour idle timeout. +- **Premiere panel / scripts:** SHA-256-hashed bearer tokens issued from `Settings → API Tokens`. + +### First-run setup + +On a fresh install with `AUTH_ENABLED=true`, navigate to the web UI in a browser. +With no users in the database, the login screen renders a "First-run setup" form +instead — fill it in to create the first admin and you are logged in immediately. + +Subsequent users are created from `Settings → Users` (any signed-in user can +create others — flat access). + +### Dev mode + +Setting `AUTH_ENABLED=false` disables all auth checks; a synthetic `dev` user +is attached to every request. **Never deploy this way.** The dev user row is +seeded with a hash that no real password can match, so flipping +`AUTH_ENABLED=true` later does not expose the dev account. + +### Recovering a forgotten admin password + +Any signed-in user can reset another user's password from `Settings → Users`. +If no one can sign in (all admins forgot their passwords), reset directly in +Postgres: + +```sql +-- generate a fresh bcrypt hash with: +-- node -e "import('bcrypt').then(b => b.default.hash(process.argv[1], 12).then(h => console.log(h)))" 'new-passphrase-here' +UPDATE users SET password_hash = '', password_updated_at = NOW() + WHERE username = 'admin'; +``` + +### `AUTH_ENABLED` transition + +When flipping `AUTH_ENABLED=false` → `true` on an existing install: + +1. Ensure `SESSION_SECRET` is set to a stable value (rotating it logs everyone out). +2. Set `ALLOWED_ORIGINS` to the public origin(s) of the web UI. +3. Set `TRUST_PROXY=true` when behind nginx (required for rate-limit accuracy). +4. Restart `mam-api`. +5. Visit the UI — first-run setup will appear if no real users exist yet. + +--- + ## License Proprietary — Wild Dragon LLC, all rights reserved. diff --git a/services/mam-api/src/index.js b/services/mam-api/src/index.js index a5b9d99..7539b44 100644 --- a/services/mam-api/src/index.js +++ b/services/mam-api/src/index.js @@ -273,7 +273,7 @@ setInterval(selfHeartbeat, 30_000); selfHeartbeat(); const server = app.listen(PORT, () => { - const authMode = process.env.AUTH_ENABLED === 'true' ? 'ENABLED' : 'DISABLED (set AUTH_ENABLED=true for production)'; + const authMode = process.env.AUTH_ENABLED === 'true' ? 'ENABLED' : 'DISABLED — dev mode (dev user attached to every request)'; console.log(`MAM API listening on port ${PORT}`); console.log(`Authentication: ${authMode}`); if (process.env.AUTH_ENABLED === 'true' && process.env.TRUST_PROXY !== 'true') {