I want to use the security.interactive_login
event to update my User's last login field.
The event is successfully registered:
php bin/console debug:event-dispatcher security.interactive_login
Registered Listeners for "security.interactive_login" Event
===========================================================
------- ------------------------------------------------------------------------ ----------
Order Callable Priority
------- ------------------------------------------------------------------------ ----------
#1 App\EventSubscriber\UserLocaleSubscriber::onSecurityInteractiveLogin() 0
------- ------------------------------------------------------------------------ ----------
But it lands on Not called listeners in the Symfony profiler.
This is the event subscriber:
class UserLocaleSubscriber implements EventSubscriberInterface
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
/** @var User $user */
$user = $event->getAuthenticationToken()->getUser();
$user->setLastLoginAt(new DateTime());
$this->em->persist($user);
$this->em->flush();
}
public static function getSubscribedEvents()
{
return [
SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin',
];
}
}
And there is my security.yaml file:
security:
enable_authenticator_manager: true
encoders:
App\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js|fonts)/
security: false
main:
lazy: true
provider: app_user_provider
guard:
authenticators:
- App\Security\LoginAuthenticator
logout:
path: app_logout
target: app_login # where to redirect after logout
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/(?!login), roles: ROLE_ADMIN }
The LoginAuthenticator class is Symfony's default generated one.
Why the interactive login event is not called?
When using the new(ish) authentication manager, the INTERACTIVE_LOGIN
event is replaced with the LoginSuccessEvent
.
# my subscriber
public static function getSubscribedEvents()
{
return [
//SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin',
LoginSuccessEvent::class => 'onLoginSuccess'
];
}
public function onLoginSuccess(LoginSuccessEvent $event)
{
$user = $event->getUser();
$user->setCount($user->getCount() + 1);
$this->em->flush();
//dd($user);
}
I'm not sure if this is explicitly documented yet. Like many upgrade deprecations, the code is very confusing. I tried to trace through what was happening and quickly got lost (once again) in the Security forest.
Events are talked about here.
I discovered this behavior by creating a fresh 5.1 project, running make:auth
and adding a listener for both events. But I forgot to add enable_authenticator_manager: true to the security config.
So the INTERACTIVE_LOGIN
event was fired. After enabling the new manager, the LoginSuccessEvent
was fired. Notice that the new event has some additional helper methods such as getUser. Makes the code a tiny bit cleaner.
Off-topic but I would caution against flushing the entity manager inside of a listener. It can be a bit unpredictable depending on what else is going on. Might consider just getting the database connection and executing a SQL update.