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>
98 lines
4.3 KiB
Markdown
98 lines
4.3 KiB
Markdown
# 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:
|
|
|
|
1. **`public/index.php` validated 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.
|
|
2. **Nothing ever destroyed the PWA's own PHP session.** The PWA
|
|
(`pwa.lupmis4luspa.org`) keeps its own `PHPSESSID` session, independent of
|
|
the central SSO portal. "Logout" cleared only the `sso_auth_token` cookie;
|
|
the `PHPSESSID` session (holding district 47) survived. Browser "clear cache"
|
|
does not delete cookies, so the stale `PHPSESSID` kept 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=1` endpoint in
|
|
`index.php` runs `session_destroy()`, expires the `PHPSESSID` cookie, clears
|
|
`sso_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()`)
|
|
|
|
```php
|
|
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=1` instead of only clearing the cookie, and wipes the
|
|
device's district-scoped SQLite caches first.
|
|
- **Stale-district guard** (`main.js` `enforceDistrictConsistency()`) — on load,
|
|
if the session's `district_id` differs from the last-seen value in
|
|
`localStorage`, 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 `curl` on 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 `PHPSESSID` cleared) once after
|
|
deploy to pick up the new district. After that the model is self-correcting.
|
|
- **Assumes the SSO `validate` endpoint returns the new district** — confirmed
|
|
via an incognito test (fresh session injected the correct `district_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.
|