ajgl/session-expiration-bundle
Install the Bundle
composer require ajgl/session-expiration-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Ajgl\SessionExpirationBundle\AjglSessionExpirationBundle::class => ['all' => true],
];
Configure Expiration
Add configuration to config/packages/ajgl_session_expiration.yaml:
ajgl_session_expiration:
idle_timeout: 300 # 5 minutes (in seconds)
absolute_timeout: 1800 # 30 minutes (optional)
check_interval: 60 # Check every 60 seconds
storage: session # or 'database' if using custom storage
First Use Case: Basic Idle Session Handling The bundle automatically tracks user inactivity. No additional code is needed for core functionality. Test by:
idle_timeout period—you should be redirected to a login page or see a session expired notice.Event-Based Integration
Listen to the session.expiration event to customize behavior (e.g., logging, notifications):
// src/EventListener/SessionExpirationListener.php
namespace App\EventListener;
use Ajgl\SessionExpirationBundle\Event\SessionExpirationEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class SessionExpirationListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
SessionExpirationEvent::NAME => 'onSessionExpiration',
];
}
public function onSessionExpiration(SessionExpirationEvent $event)
{
// Custom logic (e.g., log user out gracefully)
$event->getUser()->getRoles(); // Access user data
}
}
Custom Storage (Database Example) Extend the bundle’s storage interface to use a database:
// src/Storage/DatabaseSessionStorage.php
namespace App\Storage;
use Ajgl\SessionExpirationBundle\Storage\SessionStorageInterface;
use Doctrine\ORM\EntityManagerInterface;
class DatabaseSessionStorage implements SessionStorageInterface
{
public function __construct(private EntityManagerInterface $em) {}
public function updateLastActivity(string $sessionId, int $timestamp): void
{
$entity = $this->em->getRepository(Session::class)->find($sessionId);
$entity->setLastActivity($timestamp);
$this->em->flush();
}
public function isExpired(string $sessionId, int $idleTimeout): bool
{
$entity = $this->em->getRepository(Session::class)->find($sessionId);
return $entity && (time() - $entity->getLastActivity()) > $idleTimeout;
}
}
Register the service in config/services.yaml:
services:
App\Storage\DatabaseSessionStorage: ~
ajgl_session_expiration.storage: '@App\Storage\DatabaseSessionStorage'
Excluding Routes Skip session checks for specific routes (e.g., API endpoints) by tagging them:
# config/routes.yaml
api_login:
path: /api/login
controller: App\Controller\ApiAuthController::login
methods: POST
ajgl_session_expiration: false # Disable session check
Dynamic Timeout Adjustment Override the timeout per user or role via a listener:
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if ($request->hasPreviousSession()) {
$user = $request->getUser();
if ($user && $user->hasRole('ADMIN')) {
$this->sessionExpiration->setIdleTimeout(1800); // 30 mins for admins
}
}
}
Symfony Version Mismatch
The bundle requires Symfony 2.3+ (per composer.json). Test thoroughly if using Symfony 3/4/5—some features (e.g., event dispatching) may behave differently.
Session Storage Conflicts
The default session storage relies on PHP’s native session handling. If using custom session handlers (e.g., Redis, Memcached), ensure they support session_start() and session_write_close().
SessionStorageInterface for your storage backend.Event Dispatching Timing
The session.expiration event fires after the session is marked expired. If you need to act before (e.g., warn the user), use a kernel.request listener to manually check expiration:
public function onKernelRequest(GetResponseEvent $event)
{
if ($event->isMasterRequest() && !$this->isSessionActive($event->getRequest())) {
$event->setResponse(new RedirectResponse('/session-expired'));
}
}
Absolute Timeout Overrides Idle Timeout
If both idle_timeout and absolute_timeout are set, the session expires at the earlier of the two times. Test edge cases (e.g., a user active right before absolute_timeout).
Log Expiration Events Enable debug mode and log the event:
# config/services.yaml
Ajgl\SessionExpirationBundle\EventListener\SessionExpirationListener:
tags:
- { name: kernel.event_subscriber }
arguments:
$logger: '@logger'
Then implement logging in your subscriber.
Check Session Data Dump session data to verify the bundle is tracking activity:
// In a controller or listener
dump($_SESSION['ajgl_session_expiration_last_activity']);
Disable for Testing
Temporarily disable the bundle in config/bundles.php during tests:
Ajgl\SessionExpirationBundle\AjglSessionExpirationBundle::class => ['test' => false],
Custom Expiration Logic
Override the Ajgl\SessionExpirationBundle\SessionExpiration service to add logic:
# config/services.yaml
services:
ajgl_session_expiration:
class: App\Service\CustomSessionExpiration
parent: ajgl_session_expiration
arguments:
$storage: '@App\Storage\DatabaseSessionStorage'
Add a "Stay Logged In" Option Extend the bundle to support a "remember me" checkbox that resets the idle timer:
// In your login form handler
if ($form->isSubmitted() && $form->isValid() && $form->get('remember_me')->isClicked()) {
$this->sessionExpiration->setIdleTimeout(2592000); // 30 days
}
Integrate with Security Events
Trigger a security.interactive_login event when a session is revived after expiration:
public function onSessionExpiration(SessionExpirationEvent $event)
{
if ($event->isRevived()) {
$this->eventDispatcher->dispatch(
new InteractiveLoginEvent($event->getUser(), $event->getRequest()),
'security.interactive_login'
);
}
}
Localization Support Customize the expiration message in Twig:
{% if app.session_expired %}
{{ 'session.expired.message'|trans({
'%timeout%': ajgl_session_expiration.idle_timeout|date('i minutes')
}) }}
{% endif %}
Add translations to translations/messages.en.yaml.
How can I help you explore Laravel packages today?