Patch notes — per-device rolling sessions & mobile SSO (2026-06-24)
A small, focused release on the auth/session layer. Sessions are now rolling
(slid forward on activity instead of expiring at a fixed window from login) with
a per-device idle-timeout — 8h for desktop/admin, 14d for the mobile field
PWA — which also fixes mobile push notifications silently dropping. The mobile
login screen gains an SSO button. Deployed to main (247133b); Docker smoke
166/0. One additive schema column, applied automatically on container start —
no manual migration.
1 — Rolling per-device sessions ✅
Previously a session expired at a fixed SESSION_TTL (12h) measured from login,
and session_user() was a pure read that never extended it — so an active user
was logged out mid-use regardless of activity. Two changes fix this:
- Per-device TTL.
SESSION_TTLis now 8h (desktop / admin — often a shared or public browser), and a newSESSION_TTL_MOBILEis 14d (the field PWA runs on a worker’s personal phone). Asessions.scopecolumn ('web'|'mobile') records which applies; added with the existingALTER TABLE … ADD COLUMN IF NOT EXISTSidiom so it migrates on start. - Rolling refresh.
session_roll()pushes a still-valid session’s expiry forward by its scope’s TTL once it passes the halfway mark — so the DB write only fires in the second half of the window, not on every request.require_user()calls it and queues a refreshed cookie, emitted centrally fromend_headers(), so the cookie’sMax-Ageslides in lockstep with the server-side expiry. Idle sessions still expire on the same schedule.
Touched config.py, core/auth.py, handlers/base.py, scripts/init_db.py.
2 — Push notifications stop dropping ✅
The frequent mobile logouts had a side effect: the field PWA only (re)registers
its Web Push subscription while on its authenticated screen, so every forced
logout parked the user on the login page and let the push endpoint go stale →
410 → pruned → notifications silently died. Keeping active mobile sessions alive
(item 1) keeps the push subscription warm, so assignment/alert notifications keep
arriving. No push-code change — this is a second-order fix of the session bug.
3 — Login picks the scope; mobile SSO button ✅
- Scope detection. The mobile login form posts
device=mobile, and the SSO round-trip carriesnext=/mobile;handle_loginand the SSO callback read those to give the session the right scope/TTL. SSO from desktop staysweb/8h. - Mobile SSO sign-in. The field PWA login screen now shows a “Sign in with
PocketID” button (styled
mbtn ghost) whensso_enabledis set — the same SSO available on desktop, granting the full 14-day mobile session. Hidden when SSO is off.
Touched handlers/auth.py, handlers/mobile.py.
Verification
Docker smoke 166/0 at the release commit (247133b); py_compile clean.
Live checks against a running stack confirmed:
- Desktop login cookie
Max-Age=28800(8h),scope=web; mobile login cookieMax-Age=1209600(14d),scope=mobile. - Rolling refresh: a session pushed past its halfway mark re-stamped the cookie to full TTL and slid the DB expiry forward on the next authenticated request; a fresh (first-half) session correctly emitted no refresh (write-only-when-needed).
- The SSO button is absent with SSO off and present (
href="/auth/sso/login?next=/mobile") with SSO on.
The single additive column (sessions.scope) migrates automatically on container
start; existing logged-in sessions default to web and adopt their real scope on
next login. Rollback is a prior dated image tag.