Document the login gate and its security properties
This commit is contained in:
30
AGENTS.md
30
AGENTS.md
@@ -209,7 +209,35 @@ Each pass has quality gates. Quality is the primary concern; generation speed is
|
||||
- Never add a dependency if stdlib suffices
|
||||
- Prefer: stdlib → third-party → custom implementation
|
||||
|
||||
## Current Session Summary### Session: Distribution spec (AR + Spotify); peak gate fixed; Deal Hunter regen in flight
|
||||
## Current Session Summary### Session: Login gate on the review UI (replaces the open surface)
|
||||
- **The review UI is now authenticated.** Until 2026-10-09 it had **no auth at all**:
|
||||
`active_project` is an unsigned cookie and the server binds `0.0.0.0`, so every page -
|
||||
including mutation endpoints and audio downloads - was reachable from the LAN and,
|
||||
through the Caddy proxy, the internet. Spec 21/22 called this an unauthenticated
|
||||
mutation surface; it is now closed locally.
|
||||
- **Gate is middleware, not per-route dependencies** (`umbraaudio/web/__init__.py`), so a
|
||||
newly added route is protected by default and forgetting a decorator cannot expose the
|
||||
UI. **Fail-CLOSED**: an exception while checking a session DENIES the request. Falling
|
||||
through to "no session required" would turn a bug into an open door.
|
||||
- `umbraaudio/auth.py` - PBKDF2-HMAC-SHA256 (stdlib: the project rule is stdlib before
|
||||
third-party) + random per-user salt. Sessions are HMAC-signed, carry **no secret** (just
|
||||
username + expiry), compared with `hmac.compare_digest`. Store writes are atomic
|
||||
(tmp + `os.replace`) so an interrupted save cannot lock the user out. Both `auth.json`
|
||||
and `session_secret` are chmod 0600 under `/.umbra/`, which is **gitignored** (line 42).
|
||||
- **First use**: username `bdumbra`, one-time password `First321`, stored **hashed** and
|
||||
marked `must_change`, so the bootstrap password never lands on disk in plaintext and is
|
||||
dead after the first login. Password rule: **more than six chars, >=1 letter and >=1
|
||||
special**. Sessions 14 days, sliding.
|
||||
- Login failures are deliberately vague - wrong username and wrong password give the SAME
|
||||
message, so the form cannot enumerate accounts.
|
||||
- **19 tests** in `tests/test_auth.py`, incl. tampered/forged/expired tokens, plaintext
|
||||
absence, 0600 mode, corrupt-store recovery. Full suite **649 passed** (8 pre-existing).
|
||||
- **Deliberately separate module**: `auth.py` + `web/auth_routes.py` mirror the
|
||||
`name_token_routes` pattern, so the hot `routes.py` stays untouched and controller-delegated
|
||||
identity can replace this without touching route code.
|
||||
- End state is the **controller on oldbox** authenticating centrally (spec 21/22). Until
|
||||
then this is the local stand-in - see `oldbox` for that plan.
|
||||
### Session: Distribution spec (AR + Spotify); peak gate fixed; Deal Hunter regen in flight
|
||||
- **Distribution target: Author's Republic + Spotify for Authors.** INaudio is ruled out -
|
||||
DVN must be an unmodified LPF from Google Play Books / ElevenLabs / Spoken Press only, and
|
||||
submitting TTS as "Human Narrated" risks removal/account action. Umbra Audio is neither.
|
||||
|
||||
Reference in New Issue
Block a user