Symfony 3 listener doesn't run after logging in

417 views Asked by At

I'm building my first API login with Symfony3 but I tripped up on the login listener. I want to have an event fired as soon as the user has successfully logged in for the various, usual purposes such as write a log, generate a token etc. Because things are happening over API, the login system is a bit different from the classic login form described in Symfony guides. In this light, I'm sure there's something I missed.

Listener initialisation:

// config/services.yml
//...

    login_listener:
        class: 'User\LoginBundle\Listener\LoginListener'
        tags:
          - { name: 'kernel.event_listener', event: 'security.interactive_login', method: onSecurityInteractiveLogin }

My Listener:

// User/LoginBundle/Listener/LoginListener.php

namespace User\LoginBundle\Listener;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

class LoginListener
{
    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
      echo 'Hello, I am the login listener!!';
    }
}

my controller class

// User/LoginBundle/Controller/LoginController.php
//...

    public function checkCredentialsAction(Request $request)
    {
        $recursiveValidator = $this->get('validator');

        $user = new User;
        $user->setUsername($request->request->get('username'));
        $user->setPassword($request->request->get('password'));


        $errors = $recursiveValidator->validate($user);

        if (count($errors) > 0) {
            $errorsString = (string) $errors;

            return new JsonResponse($errorsString);
        }

        $loginService = $this->get('webserviceUserProvider.service');

        $user = $loginService->loadUserByUsernameAndPassword(
            $request->get('username'),
            $request->get('password')
        );

        if ($user instanceof WebserviceUser) {
            return new JsonResponse('all right');

        }

        return new JsonResponse('Username / password is not valid', 403);
    }

My security component

security:

    # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
    providers:
        in_memory:
            memory: ~

        api_key_user_provider:
            id: AppBundle\Security\ApiKeyUserProvider
#                property: apiKey

        user_db_provider:
            entity:
                class: UserUserBundle:User
#                property: username

        webservice:
            id: User\UserBundle\Security\User\WebserviceUserProvider

    encoders:
        User\UserBundle\Entity\User:
            algorithm: bcrypt

    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        user_logged:
            pattern: ^/logged
            stateless: true
            simple_preauth:
                authenticator: AppBundle\Security\ApiKeyAuthenticator
            provider: api_key_user_provider

        main:
            anonymous: ~
            form_login:
                check_path: login/check


    access_control:
        - { path: ^/login/check, roles: IS_AUTHENTICATED_ANONYMOUSLY }

As you can see the validation is carried out against the entity and when user / password is valid, the json returned is return new JsonResponse('all right, you are logged in');. The validation is done in a Custom User Provider class instantiated as service (method loadUserByUsernameAndPassword, which it is pretty similar to this),

Why when user and password are valid and the login occurs, the listener isn't taken into account as a valid event to make fire the interactive_login event?

1

There are 1 answers

3
Cerad On BEST ANSWER

If for some reason you really have the need to manually login an user then add this method to your controller and call at the appropriate time:

private function loginUser(Request $request, UserInterface $user)
{
    $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
    $this->get("security.token_storage")->setToken($token);

    $event = new InteractiveLoginEvent($request, $token);
    $this->get("event_dispatcher")->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $event);
}

if ($user instanceof WebserviceUser) {
    $this->loginUser($request,$user);
    return new JsonResponse('all right');
}

However, in most cases the existing authentication system (or a custom guard authenticator, https://symfony.com/doc/current/security/guard_authentication.html) will do this for you.