Session Reauthentication: Step 4 Implementation Summary

Stage 1 - Safe return targets

  • Changes:

    • Added the shared ResumeTarget normalizer for relative path-and-query return destinations.

    • Added focused coverage for valid destinations, discarded fragments, and unsafe-target fallback.

  • Verification:

    • php tests/run.php ResumeTargetTest — passed (3 tests).

  • Notes:

    • The server deliberately drops fragments; a later in-page navigation guard can retain them before navigation.

Stage 2 - Recoverable protected HTML requests

  • Changes:

    • Added the shared Reconnecting page for expired, safe protected HTML GET requests.

    • Preserved explicit Lobby/approval behavior for known pending or signed-out identities and explicit failures for APIs, downloads, RSS, and writes.

    • Added smoke coverage for Board, thread, profile, and query-string resume targets.

  • Verification:

    • php tests/run.php LocalAppSmokeTest::testClearingIdentityRevokesApprovedPrivateSession LocalAppSmokeTest::testPrivateLobbyOnlyExposesLobbyAccountAndAuthenticationSurfaces LocalAppSmokeTest::testPendingPrivateSessionNavigationOnlyShowsLobbyOwnProfileAndAccount ResumeTargetTest — passed (6 tests).

  • Notes:

    • The broader LocalAppSmokeTest run still has three pre-existing fetchActivity() arity failures unrelated to this stage.

Stage 3 - Return-aware browser authentication

  • Changes:

    • Extended private-site authentication with validated explicit and page-provided return destinations.

    • Replaced the recovery history entry after approved authentication and retained actionable missing-key and pending-approval behavior.

    • Added browser-script coverage for returning to a deep query-string destination.

  • Verification:

    • php tests/run.php PrivateSiteAuthTest — passed (5 tests).

    • node --check public/assets/private_site_auth.js — passed.

  • Notes:

    • The existing invitation destination remains the fallback when no resume destination is configured.

Stage 4 - In-page navigation recovery

  • Changes:

    • Added the read-only /api/auth_status contract and loaded a shared navigation guard for approved-member pages.

    • The guard checks the session before same-origin protected navigation, authenticates once when needed, preserves fragments, and sends unavailable-key recovery to Account.

    • Prevented automatic authentication from running on ordinary approved pages merely because the shared scripts are loaded.

  • Verification:

    • node --check public/assets/private_site_auth.js and node --check public/assets/auth_navigation.js — passed.

    • php tests/run.php AuthNavigationTest PrivateSiteAuthTest LocalAppSmokeTest::testApprovedPrivateSessionCanViewOwnProfileAndBoard LocalAppSmokeTest::testClearingIdentityRevokesApprovedPrivateSession — passed (9 tests).

  • Notes:

    • One tab suppresses duplicate clicks while authentication is in flight; parallel tabs retain the existing server-side challenge retry behavior.

Stage 5 - Persistent low-friction sign-in policy

  • Changes:

    • Made the PHP viewer-session cookie persistent for 400 days, the common browser retention ceiling, so ordinary browser restarts retain the session identifier.

    • Documented that the saved browser key, plus automatic recovery when PHP state is cleared, provides the no-user-facing-timeout policy.

    • Did not add a new feature flag or approval-revocation behavior; both are outside this cycle's implementation scope.

  • Verification:

    • php tests/run.php LocalAppSmokeTest::testPrivateViewerSessionCookiePersistsAcrossBrowserRestart LocalAppSmokeTest::testApprovedPrivateSessionCanViewOwnProfileAndBoard LocalAppSmokeTest::testClearingIdentityRevokesApprovedPrivateSession ResumeTargetTest PrivateSiteAuthTest AuthNavigationTest — passed (13 tests).

    • node --check public/assets/private_site_auth.js and node --check public/assets/auth_navigation.js — passed.

  • Notes:

    • Browser and server-session storage may still discard state; the browser key restores approved access without a manual sign-in.

    • No aggregate recovery telemetry was added because the application has no privacy-preserving telemetry sink; adding one should be a separate observability decision.