Sep 15 - Fixed login session when check 'Remember for 30 days' box

This commit is contained in:
2026-09-15 12:11:32 -04:00
parent 23bd3a897b
commit 4786f34f4d
4 changed files with 47 additions and 12 deletions
+17 -2
View File
@@ -507,7 +507,13 @@ Exception: `/qr/<url>/checkin` is CSRF-exempt (public, unauthenticated).
### Session Security
- `session.clear()` before setting new keys on login (prevents session fixation)
- `before_request` hook `adjust_session_lifetime()`: `remember_me` → 30 days, default → 10 hours
- **Remember Me** → permanent cookie, 30 days (`PERMANENT_SESSION_LIFETIME`), renewed on each request.
**Without it** → browser-session cookie; `before_request` hook `adjust_session_lifetime()` clears
the session 10 hours after `session['login_epoch']` (set in `auth.login`)
- **Never assign `app.permanent_session_lifetime` per request, and never lower
`PERMANENT_SESSION_LIFETIME` below 30 days.** Flask checks every session cookie's age against
it in `open_session()` *before* any `before_request` hook, and the value is shared by the whole
worker — setting it to 10 h for anonymous traffic (QR scans) logged Remember Me users out (Set 19)
- `SESSION_COOKIE_SECURE=True` requires HTTPS — HTTP-only deployments must set `false` or login loops
### validate_session_security() — DO NOT USE in before_request
@@ -1101,7 +1107,7 @@ exact-match test is what dropped every SP/PW/PT row the query had already return
| File | Fix |
|---|---|
| 7 route files | `Model.query.get_or_404()``db.session.get()` + `abort(404)` (21 call sites) |
| `config.py` | `PERMANENT_SESSION_LIFETIME``timedelta(hours=10)` |
| `config.py` | `PERMANENT_SESSION_LIFETIME``timedelta(hours=10)` (later restored to 30 days — the 10-hour limit now lives in `adjust_session_lifetime()`, see Set 19) |
| `routes/attendance.py` | Split into 4 files sharing one blueprint |
| `requirements.txt` | `mysql-connector-python` removed |
| `routes/attendance.py` | LIMIT 1001 + `records_truncated` flag + yellow banner |
@@ -1190,6 +1196,15 @@ exact-match test is what dropped every SP/PW/PT row the query had already return
| `templates/time_attendance_records.html` | Tooltip on the Export by Building button |
| — | Replaces the manual "Copilot" procedure (delete PM rows + SP rows, then build a weekly table). Decisions: PM IDs removed from both sheets; SP hours excluded from weekly hours |
### Set 19 — Remember Me Logged Users Out Within 30 Days (Sept 15, 2026)
| File | Fix |
|---|---|
| `app.py` | `adjust_session_lifetime()` no longer assigns `app.permanent_session_lifetime` (10 h / 30 d per request). Flask validates the cookie's age against that worker-wide value in `open_session()` before hooks run, so any request without Remember Me — including the login POST itself and anonymous QR scans — made Remember Me cookies older than 10 h unreadable; under gevent it could also stamp a 10 h expiry on them. The hook now clears non-Remember-Me sessions 10 h after `login_epoch` instead |
| `routes/auth.py` | Login sets `session['login_epoch']` |
| `config.py` | Comment corrected: `PERMANENT_SESSION_LIFETIME` stays 30 days — it is the cookie age limit for every session |
| — | Verified by loading the real hook (before/after) into a Flask app with a controlled clock: Remember Me now survives 11 h, 25 days of daily use and 29 idle days, and expires after 31 idle days; non-Remember-Me still ends at 10 h |
| — | No forced re-login on deploy: valid sessions keep working; non-Remember-Me sessions from before the deploy (no `login_epoch`) get their 10 hours counted from their first request after it |
---
## 21. Infrastructure & Deployment