Session / district correctness - public/index.php: add a /?logout=1 endpoint that destroys the PWA's own PHP session (session_destroy + expire PHPSESSID + clear sso_auth_token, then redirect to the SSO portal). Logout previously cleared only the SSO cookie, leaving the PHPSESSID session — and its frozen district_id — intact, which is why a reassigned user kept loading the old district across logout/login. - SSO token is validated once per session, at login (unchanged first-login logic). A district transfer is now picked up on the next logout→login, which is correct precisely because logout finally tears the session down. No periodic SSO polling. - main.js: the menu Logout button routes through /?logout=1 and wipes district-scoped local caches first. Logout is blocked while offline — a session can only be created online, so an offline logout would strand the user with no way back in (and would not actually reach the server). - main.js: enforceDistrictConsistency() clears district-scoped caches when the session district changes between loads; the district boundary is cached under a per-district key (district_boundary_<id>) so one district's geometry can never be served for another. GPS coordinate format - New "GPS Coordinate Format" setting (Lat/Lon · UTM · Both) in the Settings panel; the navbar read-out renders the chosen format and repaints the current fix immediately on change. Self-contained WGS84→UTM converter in geo-utils.js, verified against an independent Redfearn-series implementation. ol-ext touch cursor - MapView gates the TouchCursor to genuine touch-only devices via matchMedia (any-pointer: fine / any-hover: hover); hybrid touchscreen laptops keep the normal cursor. Reactive to pointer-capability changes. - Service worker v11 → v12 (new shell). docs/SSO_Session_Refresh_Proposal.md documents the implemented approach. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.3 KiB
District-reassignment fix — implemented
Status: Implemented in public/index.php, main.js, index.html, public/sw.js (v12).
Approach chosen: validate-once-per-session + destroy-session-on-logout (no periodic TTL polling).
1. The bug
When an administrator reassigned a user to a different district (e.g. 47 → 238), the PWA kept navigating to the old district even after logout, cache clear, and re-login.
Two compounding causes:
public/index.phpvalidated the SSO token only when no PHP session existed. Once$_SESSION['user_id']was set, the user's fields (district_id, …) were frozen for the life of that PHP session.- Nothing ever destroyed the PWA's own PHP session. The PWA
(
pwa.lupmis4luspa.org) keeps its ownPHPSESSIDsession, independent of the central SSO portal. "Logout" cleared only thesso_auth_tokencookie; thePHPSESSIDsession (holding district 47) survived. Browser "clear cache" does not delete cookies, so the stalePHPSESSIDkept being reused — and because a session still existed, SSO was never re-validated.
Net effect: district 47 stayed pinned until the PHPSESSID session was
physically destroyed (which nothing did).
2. The fix (as implemented)
A district changes only on transfer — a rare event — so the validated session is correct for the entire session. We therefore do not poll SSO on a timer. Instead:
- Validate once per session, at login — unchanged first-login validation in
index.php(if (!isset($_SESSION['user_id']) && isset($_COOKIE['sso_auth_token']))). - Destroy the session on logout — a new
/?logout=1endpoint inindex.phprunssession_destroy(), expires thePHPSESSIDcookie, clearssso_auth_token, and redirects to the LUSPA portal.
A transferred user logs out → the PHP session is destroyed → on the next login
there is no session, so SSO is re-validated and the new district_id is
fetched. No periodic SSO calls; the session stays valid the whole time the user
is working.
Logout endpoint (public/index.php, immediately after session_start())
if (isset($_GET['logout'])) {
$_SESSION = [];
if (ini_get('session.use_cookies')) {
$cp = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$cp['path'], $cp['domain'], $cp['secure'], $cp['httponly']);
}
session_destroy();
setcookie('sso_auth_token', '', time() - 3600, '/', '.lupmis4luspa.org');
header('Location: https://lupmis4luspa.org/', true, 302);
exit;
}
The SSO validation block below it is the original first-login-only logic.
3. Client-side changes
- Logout button (
index.html/main.js) — the menu's "Logout" button now routes through/?logout=1instead of only clearing the cookie, and wipes the device's district-scoped SQLite caches first. - Stale-district guard (
main.jsenforceDistrictConsistency()) — on load, if the session'sdistrict_iddiffers from the last-seen value inlocalStorage, all district-scoped caches (boundary, UPN grid, parcels, zones, roads) are cleared so the map repopulates for the new district. - District-keyed boundary cache — the boundary is cached as
district_boundary_<id>, so one district's geometry can never be served for another.
4. Why not a periodic TTL
An earlier draft re-validated SSO on a 5-minute TTL. Rejected because:
- District changes are rare (transfers only), so polling SSO on every load
wastes a blocking
curlon the page's critical path for no benefit almost all of the time. - The logout→login cycle is the natural, explicit moment a transfer takes effect, and it is now correct because logout destroys the session.
5. Operational notes
- Existing stale sessions: a transferred user who is still on an old session
must log out and log back in (or have their
PHPSESSIDcleared) once after deploy to pick up the new district. After that the model is self-correcting. - Assumes the SSO
validateendpoint returns the new district — confirmed via an incognito test (fresh session injected the correctdistrict_id: 238). - Edge case: switching to a different SSO user without logging out of the PWA first would reuse the old PHP session. The Logout button is the supported path; this matches the original single-user-per-device assumption.