feat(auth): add optional admin PIN for all API writes

Off by default. Once a PIN is set in Settings -> System & backup, every non-GET /api/*
request (plus GET /api/ammc/config) needs a signed 30-day HttpOnly cookie, which also
closes the unauthenticated /api/update/run restart trigger. A 401 opens a PIN dialog
and the pending save is retried after login. Reads stay open because the public overlay
hydrates through GET /api/state. Optional RC_ADMIN_ALLOWED_CIDRS restricts the same
routes by client address.

The PIN hash and signing secret live in data/, outside the app_state blob, so they
never leak through GET /api/state, exports or backups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
larssand
2026-09-24 22:14:58 +02:00
parent bcd6e12a6c
commit 9016ce02e4
20 changed files with 627 additions and 8 deletions

View File

@@ -219,6 +219,17 @@ Uses:
- `logs/server.out.log`
- `logs/server.err.log`
### `server/auth.js`
Role:
- optional admin PIN protection (CommonJS, required by `server.js`)
Owns:
- PIN hashing/verification (scrypt) in `data/auth.json` and the per-installation HMAC secret in `data/auth_secret` - both outside `app_state` on purpose
- signed 30-day login cookie (`rc_auth`), invalidated when the PIN changes
- login throttling per client address
- `middleware`: once a PIN exists, rejects unauthenticated non-GET `/api/*` requests and `GET /api/ammc/config` with `401 { error: "auth_required" }`; applies `RC_ADMIN_ALLOWED_CIDRS` (403 `ip_not_allowed`) to the same routes regardless of PIN
- routes wired in `server.js`: `GET /api/auth/status`, `POST /api/auth/login`, `/logout`, `/pin`, `/disable`
### `scripts/record-video-guides.mjs` and `scripts/capture-screenshots.mjs`
Role:
- documentation tooling - not run in production, not imported by `server.js` or any `src/*.js` module
@@ -531,6 +542,15 @@ Imported by:
- `src/settings.js` for the "Banlayouter" panel (add/list/delete, delete blocked while any session still references the layout)
- `src/results_archive_view.js` to show which layout a completed session ran on
### `src/auth_view.js`
Type:
- admin PIN UI
Owns:
- `installAuthFetchGuard`: wraps `window.fetch` once at startup (`app.js`) so a `401 auth_required` from `/api/*` opens the PIN dialog; `app.js` retries the pending sync after a successful login
- `showPinDialog`: the login modal
- `mountAuthSettings`: the "Admin PIN" panel in Settings -> System & backup (enable, change, log out, disable)
### `src/misc_views.js`
Type:
- misc page renderers