TASK-033: Remove session token from JSON response body
Fixes a critical security vulnerability where the session token was being returned in the JSON response body of POST /api/auth/login. This exposed the token to JavaScript, allowing malicious scripts to steal it and bypass the HttpOnly cookie protection. Changes: - Backend: Remove 'token' field from LoginResponse model (auth.py) - Backend: Update login() endpoint to return only 'expires_at' - Frontend: Update LoginResponse type to exclude 'token' field - Backend: Update test helper _login() to extract token from cookie - Backend: Update test cases to verify token is NOT in response body - Documentation: Add section 'Authentication Endpoints' in Backend-Development.md - Documentation: Update Web-Development.md to explain HttpOnly cookie benefits Security benefit: Session tokens are now only accessible via HttpOnly cookies, protected from JavaScript access, XSS attacks, and malicious third-party scripts. The frontend continues to use only the cookie for authentication. All auth tests pass (23 tests). Type checking and linting pass with zero errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,37 +1,3 @@
|
||||
## TASK-032 — `geo_cache` table grows unboundedly — no eviction or purge
|
||||
|
||||
**Severity:** Medium
|
||||
|
||||
### Where found
|
||||
`backend/app/repositories/geo_cache_repo.py` — has `upsert_entry`, `bulk_upsert_entries`, `upsert_neg_entry` — but **no DELETE functions**. `backend/app/db.py` — `geo_cache` table has no `last_seen` or `created_at` column.
|
||||
|
||||
### Why this is needed
|
||||
Every unique IP address ever seen by fail2ban gets a row in `geo_cache`. The table is never trimmed. A BanGUI instance monitoring a busy server can accumulate millions of rows over months, increasing the DB file size and degrading query performance on every geo lookup.
|
||||
|
||||
### Goal
|
||||
Implement a retention policy that prunes geo cache entries not referenced recently.
|
||||
|
||||
### What to do
|
||||
1. Add a migration (`_MIGRATIONS[2]`) that adds a `last_seen TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP` column to `geo_cache`.
|
||||
2. Update `upsert_entry` and `bulk_upsert_entries` to set `last_seen = CURRENT_TIMESTAMP` on every upsert.
|
||||
3. Add `delete_stale_entries(db: aiosqlite.Connection, cutoff_iso: str) -> int` to `geo_cache_repo.py`.
|
||||
4. Create `backend/app/tasks/geo_cache_cleanup.py` — a nightly task that calls `delete_stale_entries` with a 90-day cutoff.
|
||||
5. Register the task in `startup_shared_resources`.
|
||||
|
||||
### Possible traps and issues
|
||||
- Adding a column requires a migration. Coordinate with TASK-023 (migration atomicity) and TASK-022 (session hash migration) — all three migrations must be sequenced correctly as `_MIGRATIONS[2]`, `[3]`, etc.
|
||||
- IPs that have not been seen in 90 days will lose their geo data — on their next appearance they will be re-resolved from ip-api.com or the MMDB. This is acceptable.
|
||||
|
||||
### Docs changes needed
|
||||
- `Architekture.md` — update the `geo_cache` table description and add the cleanup task.
|
||||
- `Backend-Development.md` — document the geo cache retention policy.
|
||||
|
||||
### Doc references
|
||||
- [Architekture.md](Architekture.md) — application database schema
|
||||
- [Backend-Development.md](Backend-Development.md) — background tasks
|
||||
|
||||
---
|
||||
|
||||
## TASK-033 — Session token returned in JSON body alongside HttpOnly cookie
|
||||
|
||||
**Severity:** Medium
|
||||
|
||||
Reference in New Issue
Block a user