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?
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:
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.