← Back

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_TTL is now 8h (desktop / admin — often a shared or public browser), and a new SESSION_TTL_MOBILE is 14d (the field PWA runs on a worker’s personal phone). A sessions.scope column ('web' | 'mobile') records which applies; added with the existing ALTER TABLE … ADD COLUMN IF NOT EXISTS idiom 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 from end_headers(), so the cookie’s Max-Age slides 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 carries next=/mobile; handle_login and the SSO callback read those to give the session the right scope/TTL. SSO from desktop stays web/8h.
  • Mobile SSO sign-in. The field PWA login screen now shows a “Sign in with PocketID” button (styled mbtn ghost) when sso_enabled is 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 cookie Max-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.