Search code examples
symfonysymfony-3.4symfony-security

UserChecker - Use entity manager


My website is running Symfony 3.4 and I made my own user member system. My User entity contains a Datetime field 'lastLogin' and I can't find a solution to update it every time a user logged in.

I created a custom UserChecker then I tried to update the field in it :

<?php

namespace CoreBundle\Security;

use CoreBundle\Entity\User as AppUser;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class UserChecker implements UserCheckerInterface
{
    public function checkPreAuth(UserInterface $user)
    {
        if (!$user instanceof AppUser) {
            return;
        }
        if ( $user->getDeleted() || !$user->getEnabled()  )
        {
            throw new AuthenticationException();
        }
        else
        {
            // BELOW IS WHAT I TRY, BUT FAIL.
            $entityManager = $this->get('doctrine')->getManager();
            $user->setLastLogin(new \DateTime());
            $entityManager->persist($user);
            $entityManager->flush();
        }
    }

    public function checkPostAuth(UserInterface $user)
    {
        if (!$user instanceof AppUser) {
            return;
        }
    }
}

But it doesn't work. Maybe I can't use the doctrine entity manager in this file ?

If I use $this->get('doctrine')->getManager(); I get :

Fatal Error: Call to undefined method CoreBundle\Security\UserChecker::get()


Solution

  • Dunno why @doncallisto removed his post. It was (IMHO) the right thing.

    Take a look at http://symfony.com/doc/current/components/security/authentication.html#authentication-success-and-failure-events

    So you have several options.

    • SecurityEvents::INTERACTIVE_LOGIN - triggers every time the user full out the login form and submit credentials. Will work, but you won't get last_login updates if you have remember_me cookie or similar

    • AuthenticationEvents::AUTHENTICATION_SUCCESS - triggers each time (every request) when authentication was successful. It means your last_login will be updated each time on every request unless user logged out

    so you'll need a EventSubscriber. Take a look at this article. https://thisdata.com/blog/subscribing-to-symfonys-security-events/

    MAybe you'll need a simplified version.

    public static function getSubscribedEvents()
    {
            return array(
                // AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthenticationFailure', // no need for this at that moment
                SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin', // this ist what you want
            );
    }
    

    and then the onSecurityInteractiveLogin method itself.

    public function onSecurityInteractiveLogin( InteractiveLoginEvent $event )
    {
        $user = $this->tokenStorage->getToken()->getUser();
        if( $user instanceof User )
        {
          $user->setLastLogin( new \DateTime() );
          $this->entityManager->flush();
        }
    }
    

    P.S. FosUserBundle uses interactive_login and a custom event to set last_login on entity look at: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/EventListener/LastLoginListener.php#L63