Why is Symfony Guard triggering a "security.interactive_login" event on every request?

I have a Symfony 3.2 application that provides REST API and uses Json Web Tokens (JWT) for authentication. I recently switched to using the Symfony Guard component. Mine now security.yml

contains a firewall config section like this (I'm using Lexik JWT 2.4.0 package, but it doesn't matter):

firewalls:
    # ...
    api:
        pattern:   ^/api
        stateless: true
        guard:
            authenticators:
               - lexik_jwt_authentication.jwt_token_authenticator

      

Since I made this switch, I noticed that every request is handled as if the user had just logged in, i.e. an event security.interactive_login

. The docs ( http://symfony.com/doc/current/components/security/authentication.html#authentication-events ) says:

The security.interactive_login event is triggered after active use by a user logged into your site. It is important to distinguish this action from non-interactive authentication methods such as remember me cookie-based authentication, session-based authentication, authentication using the main HTTP header or HTTP header. You can listen for the security.interactive_login event, for example, to give your user a welcome flash message every time they log in.

So I definitely don't expect this event for every request - I would rather get an event security.authentication.success

for every request as stated in the docs.

However, the Symfony class GuardAuthenticatorHandler

dispatches an event security.interactive_login

to its method authenticateWithToken

, and this method is called GuardAuthenticationListener

for every request. Is this a bug in Symfony, misunderstanding on my side or misconfiguration?

(This is not a philosophical question - in my case it leads to a specific problem that the user's last login time is updated on every request, which doesn't make sense.)

+4


source to share


4 answers


You must change this



        stateless: false

      

+1


source


I ran into your problem because I have exactly the same problem. My workaround is to add the attribute to the request object, right before returning true in the backing guard method.

Example:

public function supports(Request $request)
{
    ...

    $request->attributes->set('is_interactive_login', true);

    return true;
}

      



With this information you can check if it was an interactive input to an event listener

Example:

public function onLoginSuccess(InteractiveLoginEvent $event)
{
    $request = $event->getRequest();
    if ($request->attributes->get('is_interactive_login', false)) {
        // do whatever you need todo on interactive login
    }
}

      

+1


source


It is better to subscribe to the event Events::JWT_CREATED

as it is Events::JWT_CREATED

after being authenticated with credentials.

Example:

<?php

namespace App\Event\Subscriber;

use App\Entity\User\User;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Events;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class AuthenticationSuccessSubscriber implements EventSubscriberInterface
{
    /**
     * @var EntityManager
     */
    private $em;

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public static function getSubscribedEvents()
    {
        return [
            Events::JWT_CREATED => 'onInteractiveLogin',
        ];
    }

    /**
     * @param JWTCreatedEvent $event
     *
     * @throws \Doctrine\ORM\ORMException
     * @throws \Doctrine\ORM\OptimisticLockException
     */
    public function onInteractiveLogin(JWTCreatedEvent $event)
    {
        /** @var User $user */
        $user = $event->getUser();
        $user->setLastLoginAt(new \DateTime());
        $user->resetFailedLogins();
        $this->em->flush($user);
    }
}

      

0


source


I faced the same problem. In my case, I am using Guard authentication for API requests. So I definitely don't like updating user last_login after any API request.

The INTERACTIVE_LOGIN event is dispatched from here .

So my dirty hack is to add this definition to services

the application config section :

security.authentication.guard_handler:
        class: Symfony\Component\Security\Guard\GuardAuthenticatorHandler
        arguments:
            $eventDispatcher: ~

      

Warning

The obvious downside to this approach is that you have broken the change handler for all of your application defenders.

0


source







All Articles