docs(l4d2-web): design auth pages

This commit is contained in:
mwiegand 2026-05-06 12:40:15 +02:00
parent 07495f9319
commit 08b32bb26f
No known key found for this signature in database

View file

@ -0,0 +1,101 @@
# l4d2 Web Auth Pages Design
Date: 2026-05-06
## Goal
Replace the current plain-text login/signup placeholders with a small, functional authentication UI for the Flask web app. Keep the current username/password schema for now, remove public signup, and redirect users back to the page they originally wanted after login when that target is safe.
## Scope
In scope:
- Real server-rendered `/login` page using the existing neutral shell and CSS tokens.
- Username/password login using the existing `users.username` and `users.password_digest` fields.
- Removal of `/signup` handlers, links, and CSRF exemptions.
- Redirect anonymous protected requests to `/login?next=<wanted-path>`.
- Redirect successful logins to a safe local `next` target, otherwise `/dashboard`.
- Redirect `/` to `/login` for anonymous users and `/dashboard` for logged-in users.
- Keep `/health` public for monitoring.
Out of scope:
- Email-based login.
- Steam login.
- Account self-registration.
- Password reset or password change flows.
- User invitation flows.
- Schema changes to split users, credentials, and external identities.
## Routing Behavior
`GET /login` renders the login page. The page accepts an optional `next` query parameter and preserves it in a hidden form field so a successful POST can return the user to the requested page.
`POST /login` validates the username and password with the existing password hash helpers. On success, it stores `session["user_id"]` and redirects to the safe local `next` value if present. If `next` is missing, empty, or unsafe, it redirects to `/dashboard`.
`GET /signup` and `POST /signup` are removed. They should return Flask's normal `404 Not Found` response.
`GET /` redirects to `/login` for anonymous users and `/dashboard` for logged-in users.
Protected page decorators keep their current authorization semantics, but anonymous redirects include the requested path in `next`. Logged-in non-admin users still receive `403 Forbidden` for admin-only pages.
## Safe Redirect Rules
A `next` target is safe only when it is a local absolute path:
- It must start with `/`.
- It must not start with `//`.
- It must not contain a URL scheme such as `https://`.
Unsafe values are ignored and replaced with `/dashboard`. This avoids open redirects while keeping the implementation simple.
## UI Behavior
The login page should be minimal and consistent with the current admin-console UI:
- Brand/title: `left4me`.
- Short explanatory text.
- Fields: `username` and `password`.
- Submit button: `log in`.
- No signup link.
The shared shell should not show main application navigation to anonymous users. Anonymous users may see the brand only. Logged-in users keep the existing navigation and account controls.
## Data Model
No schema change is planned for this iteration. `User.username` remains the local login identifier. This intentionally defers email-based and Steam-based identity modeling until those flows are designed.
Future Steam login should use a separate identity model rather than forcing Steam users into an email/password shape, but that is not part of this change.
## Error Handling
Invalid credentials keep the current simple behavior: `401 invalid credentials`.
Missing username or password also remain simple response errors. The first UI pass does not need inline validation messages or flashed form errors.
Rate limiting on `POST /login` remains unchanged.
## Testing
Tests should cover:
- `GET /login` renders a form with username and password inputs.
- `GET /signup` returns `404`.
- `POST /signup` returns `404` rather than being intercepted by CSRF handling.
- Anonymous protected pages redirect to `/login?next=<path>`.
- A successful login with a safe `next` redirects to that target.
- A successful login with an unsafe `next` redirects to `/dashboard`.
- Anonymous `/` redirects to `/login`.
- Logged-in `/` redirects to `/dashboard`.
- Non-admin users still receive `403` for admin pages.
## Verification
Run these checks after implementation:
```bash
pytest l4d2web/tests/test_auth.py -q
pytest l4d2web/tests/test_pages.py -q
pytest l4d2web/tests/test_security.py -q
pytest l4d2web/tests -q
```