feat(session): preserve intended URL across session timeout and add expiry warning dialog

When a session expires, the user's current URL is now captured and restored
after re-authentication (via remember-me cookie or manual login), instead of
always redirecting to the dashboard. A JavaScript session warning dialog
appears ~2 minutes before idle timeout, allowing users to extend their session
with a single click. Includes open-redirect prevention via URL validation,
Microsoft SSO callback support, and 33 unit tests.

Refs: SESSION-REDIRECT-PRESERVE-001

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 14:28:49 +01:00
parent a3045fa95e
commit 04995e3712
18 changed files with 1333 additions and 7 deletions

View File

@@ -1,5 +1,6 @@
<?php
use MintyPHP\Http\IntendedUrlService;
use MintyPHP\Http\RequestRuntimeInterface;
use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Router;
@@ -12,13 +13,21 @@ use MintyPHP\Support\Flash;
$requestRuntime = app(RequestRuntimeInterface::class);
$sessionStore = app(SessionStoreInterface::class);
$intendedUrlService = app(IntendedUrlService::class);
// Store return_to from query parameter (set by session timeout or auth guard redirect).
$returnTo = trim((string) (requestInput()->queryAll()['return_to'] ?? ''));
if ($returnTo !== '') {
$intendedUrlService->store($returnTo, $sessionStore);
}
$session = $sessionStore->all();
if (!empty($session['user']['id'])) {
if (Flash::has()) {
Flash::keep();
}
Router::redirect("admin");
Router::redirect($intendedUrlService->retrieveAndForget($sessionStore));
return;
}
@@ -314,7 +323,7 @@ if (requestInput()->method() === 'POST') {
}
$sessionStore->set('session_started_at', time());
$sessionStore->set('session_last_activity', time());
Router::redirect('admin');
Router::redirect($intendedUrlService->retrieveAndForget($sessionStore));
return;
}
}