Install the Package
composer require ajgl/session-expiration
For Symfony bundles:
composer require ajgl/session-expiration-bundle
Register the Listener
Subscribe the SessionExpirationListener to the kernel.response event in your Symfony kernel (e.g., Kernel.php):
// src/Kernel.php
protected function build(RequestContext $requestContext): void
{
$this->bus->subscribeTo('kernel.response', SessionExpirationListener::class);
}
Configure Idle Timeout
Set the idle timeout (in seconds) via config (e.g., config/packages/ajgl_session_expiration.yaml):
ajgl_session_expiration:
idle_timeout: 1800 # 30 minutes
Or programmatically:
$listener = new SessionExpirationListener();
$listener->setIdleTimeout(1800); // 30 minutes
First Use Case Test by navigating to a protected route, leaving the browser idle, and verifying the session expires after the configured timeout.
Event-Driven Session Check
The listener triggers on every kernel.response event, checking if the session is idle (no activity) and expired.
Integration with Symfony Security
SessionExpirationListener to customize behavior (e.g., logging, redirects, or custom responses).Activity Tracking
lastActivity attribute in the session storage.// src/EventListener/ActivityListener.php
public function onKernelRequest(GetResponseEvent $event)
{
if ($event->isMasterRequest() && $this->security->isGranted('IS_AUTHENTICATED')) {
$event->getRequest()->getSession()->set('lastActivity', time());
}
}
Bundle Integration
For Symfony bundles, enable the bundle in config/bundles.php:
return [
// ...
Ajgl\SessionExpirationBundle\AjglSessionExpirationBundle::class => ['all' => true],
];
Configure via config/packages/ajgl_session_expiration.yaml.
Custom Responses Override the default behavior (e.g., redirect to login) by extending the listener:
class CustomSessionExpirationListener extends SessionExpirationListener
{
public function onKernelResponse(FilterResponseEvent $event)
{
if ($this->isSessionExpired($event->getRequest())) {
$event->setResponse(new RedirectResponse('/custom-expired-page'));
}
}
}
Session Storage Compatibility
symfony/session) supports the lastActivity attribute.Event Order Matters
kernel.response event fires after the response is built. If you need to modify the response early (e.g., abort), use kernel.controller or kernel.exception instead.Double Event Subscription
EventDispatcher to check for existing subscribers:
$dispatcher = $this->get('event_dispatcher');
if (!$dispatcher->hasListeners('kernel.response') || !$dispatcher->hasSubscriber(SessionExpirationListener::class)) {
$dispatcher->addListener('kernel.response', [new SessionExpirationListener(), 'onKernelResponse']);
}
Time Zone Awareness
lastActivity timestamp uses the server’s time zone. For consistency, ensure all activity updates use time() (UTC) or normalize to a specific time zone.CSRF Token Issues
$event->getRequest()->getSession()->regenerateId(true); // Force new session ID
Log Session Activity
Add debug logs to track lastActivity updates:
public function onKernelResponse(FilterResponseEvent $event)
{
$session = $event->getRequest()->getSession();
$this->logger->debug('Last activity:', ['timestamp' => $session->get('lastActivity')]);
// ... rest of the logic
}
Test with Short Timeout
Set idle_timeout: 10 in config to quickly test expiration behavior during development.
Check for Silent Failures
kernel.response.lastActivity attribute is being updated on every request.Custom Expiration Logic Extend the listener to implement dynamic timeouts (e.g., based on user roles):
public function getIdleTimeout(Request $request)
{
$user = $this->security->getUser();
return $user && $user->isAdmin() ? 3600 : 1800; // Admins get 1 hour
}
Grace Period for Inactivity Add a grace period before expiration (e.g., warn users 5 minutes before timeout):
public function onKernelResponse(FilterResponseEvent $event)
{
$session = $event->getRequest()->getSession();
$inactivity = time() - $session->get('lastActivity', 0);
if ($inactivity > $this->getIdleTimeout() - 300) { // 5-minute warning
$this->addWarningToResponse($event->getResponse());
}
}
Integration with Symfony Guard
For Guard-authenticated users, ensure the lastActivity is updated in the authenticated event:
$event->getRequest()->getSession()->set('lastActivity', time());
Session Revival Allow users to revive expired sessions via a "Stay Logged In" button:
public function reviveSession(Request $request)
{
$request->getSession()->set('lastActivity', time());
return new JsonResponse(['status' => 'revived']);
}
How can I help you explore Laravel packages today?