Roadmap #6: Password-manager-friendly login form #106

Closed
opened 2026-07-11 07:16:05 +00:00 by coding-agent-marvin8 · 1 comment
coding-agent-marvin8 commented 2026-07-11 07:16:05 +00:00 (Migrated from codeberg.org)

The web UI currently uses HTTP Basic Auth, which modern password managers handle inconsistently and which does not support a proper logout flow. A standard HTML login form backed by a signed session cookie would be far more compatible with password managers and would give users an explicit log-out action.

The web UI currently uses HTTP Basic Auth, which modern password managers handle inconsistently and which does not support a proper logout flow. A standard HTML login form backed by a signed session cookie would be far more compatible with password managers and would give users an explicit log-out action.
coding-agent-marvin8 commented 2026-07-11 07:16:36 +00:00 (Migrated from codeberg.org)

Plan of attack

Replaced the HTTPBasicAuth dependency with a stateless HMAC-SHA256 signed cookie. The secret key is derived from the configured password (SHA-256(password.encode())), so rotating the password automatically invalidates all existing sessions.

Key files:

  • src/cang/web/auth.py — complete rewrite; exports login_router (GET/POST /login, GET /logout), require_auth dependency, and NotAuthenticatedException; cookie name: cang_session, value: auth.<hmac-hex>
  • src/cang/web/app.py — registers login_router without auth dependency; adds @app.exception_handler(NotAuthenticatedException) → 303 to /login?next=<path>; adds auth_enabled Jinja2 global
  • src/cang/web/templates/login.html — new login form with autocomplete="username" and autocomplete="current-password" for password manager compatibility
  • src/cang/web/templates/base.html — conditional Log out link when auth_enabled
  • Open redirect prevention: _safe_next validates the next param must start with / and not //
**Plan of attack** Replaced the `HTTPBasicAuth` dependency with a stateless HMAC-SHA256 signed cookie. The secret key is derived from the configured password (`SHA-256(password.encode())`), so rotating the password automatically invalidates all existing sessions. Key files: - `src/cang/web/auth.py` — complete rewrite; exports `login_router` (GET/POST /login, GET /logout), `require_auth` dependency, and `NotAuthenticatedException`; cookie name: `cang_session`, value: `auth.<hmac-hex>` - `src/cang/web/app.py` — registers `login_router` without auth dependency; adds `@app.exception_handler(NotAuthenticatedException)` → 303 to `/login?next=<path>`; adds `auth_enabled` Jinja2 global - `src/cang/web/templates/login.html` — new login form with `autocomplete="username"` and `autocomplete="current-password"` for password manager compatibility - `src/cang/web/templates/base.html` — conditional Log out link when `auth_enabled` - Open redirect prevention: `_safe_next` validates the `next` param must start with `/` and not `//`
Sign in to join this conversation.
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marvin8/cang#106
No description provided.