Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony3, set user last login date

Tags:

php

symfony

I have a simple login form:

public function loginAction(Request $request)
{
    $authenticationUtils = $this->get('security.authentication_utils');

    // get the login error if there is one
    $error = $authenticationUtils->getLastAuthenticationError();

    // last username entered by the user
    $lastUsername = $authenticationUtils->getLastUsername();

    return $this->render(
        'users/login.html.twig',
        [
            'page' => 'login',
            'last_username' => $lastUsername,
            'error'         => $error,
        ]
    );
}

security.yml:

secured_area:
     form_login:
        provider: user_db_provider
        login_path: /login
        check_path: /login
        csrf_token_generator: security.csrf.token_manager

And I'm trying to set the user's last login time.

Do I really have to create a new listener and write an entire class, like this answer, just for this single instruction: $user->setLastLogin(new \DateTime());? Or is there something simpler, like putting it in the controller?

like image 538
the_nuts Avatar asked Jun 04 '16 12:06

the_nuts


2 Answers

Yes, you must create a login success listener, as described in this answer, and use the onAuthenticationSuccess method to store your user information.

The code you posted is for generating the login page itself. Any login action is handled by the firewall, which calls either a success or failure listener. You can execute your code (for example setting the last successful login timestsamp) in your custom listener.

The reason for that is that it is better to create classes that perform a single task, rather than putting a lot of code together in one class. This maintains your modularity, extensibility and maintanability of your code.

like image 158
Hidde Avatar answered Nov 10 '22 17:11

Hidde


Well, in Symfony 4 I'm using another approach, since I don't have FOSUserBundle in my project and I don't like use onAuthenticationSuccess cause is called every time

(..)beware - this event (onAuthenticationSuccess) will fire, for example, on every request if you have session-based authentication (..)

the user is checked for authentication ( see this security authentication section). I know that is possible to use security.interactive_login EventListener (better way), but for other reasons I prefer to do with the steps above.

So I created a simple route on SecurityController to check and update User->lastLogin attribute.

// /config/packages/security.yaml
security:
    firewalls:
        main:
        pattern: ^/
        user_checker: App\Security\UserChecker
        form_login:
            provider: my_db_provider
            login_path: login
            check_path: login
            always_use_default_target_path: true
            default_target_path: check_last_login
            csrf_token_generator: security.csrf.token_manager

Here, always_use_default_target_path: true and default_target_path: check_last_login are the keys always_use_default_target_path: true forces the target and check_last_login is the route name to redirect when actually user logs in

// /src/Controller/SecurityController.php
/**
 * @param Request $request
 * @Route("/check-last-login", name="check_last_login")
 * @param Request $request
 * @return Response
 *
 */
public function checkLastLogin(Request $request)
{
    /* @var $user User */
    if (!$user = $this->getUser()) {
        // redirect to somewhere to show some message
        throw new someAuthenticationException();
    }
    if ($user->getLastLogin() == null) {
        // Do something when is the first login
        return $this->redirectToRoute('complete-register');
    }
    $user->setLastLogin(new \DateTime());
    $oManager = $this->getDoctrine()->getManager();
    $oManager->persist($user);
    $oManager->flush();
    // 
    return $this->forward('App\Controller\DefaultController::index', array($request));
}

The forward() can be replaced to

// almost the time $providerKey is 'main' - see your firewalls entries
$providerKey = 'main';
$targetPath = $request->getSession()->get('_security.'.$providerKey.'.target_path');
$this->redirect($targetPath);

But keep in mind that can be the logout route, so you will need to avoid a successfull login followed by a logout

like image 37
Marcos Regis Avatar answered Nov 10 '22 16:11

Marcos Regis