How to authenticate (login) manually an User in a controller (with or without FOSUserBundle) on Symfony 3

Tools like FOSUserBundle makes the things really easy when we talk about the manipulation of users, login and registration forms etc, who can't say no to that ? Probably those who needs to have everything under control and to understand how this works. That's why some developers even if they don't use FOSUserBundle, they prefer to handle those events manually.

Looking for login your user with your own code from a controller ? You have found the right place to learn it. In this article you will learn how to sign in an user automatically on your Symfony application (with or without credentials).

How to do it

The first you need to do is to import some classes namely InteractiveLoginEvent and UsernamePasswordToken in your controller. Then find your user by yourself using the entity manager of Doctrine or alternatively use the user manager of FOSUserBundle. The user should be stored in a variable and will represent an instance of FOS\UserBundle\Model\User in case you're using the FOSUser or simply User if you don't. As next proceed to create a token and set in the token storage service. The class to create the token expects as first argument the user object, as second argument usually is the password of the user however it can be null. As third argument you need to provide the name of the firewall to use, it's usually main however its worth to check your security.yml file in case it has another name. As fourth argument you need to provide the roles of the user.

You need as well store the token generated by the class in the session and finally dispatch the interactive login event manually:

<?php

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

class SomeController extends Controller
{
    public function loginAction(Request $request)
    {
        $user = /*The user needs to be registered */;#
        // Example of how to obtain an user:
        //$user = $this->getDoctrine()->getManager()->getRepository("AppBundle/Entity/User")->findOneBy(array('username' => "some user name example"));
        
        //Handle getting or creating the user entity likely with a posted form
        // The third parameter "main" can change according to the name of your firewall in security.yml
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->get('security.token_storage')->setToken($token);

        // If the firewall name is not main, then the set value would be instead:
        // $this->get('session')->set('_security_XXXFIREWALLNAMEXXX', serialize($token));
        $this->get('session')->set('_security_main', serialize($token));
        
        // Fire the login event manually
        $event = new InteractiveLoginEvent($request, $token);
        $this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
        
        /*
         * Now the user is authenticated !!!! 
         * Do what you need to do now, like render a view, redirect to route etc.
         */
    }
}

Done this, you have logged automatically an user on the session, however you didn't checked for the credentials of the user.

Example custom login controller

Let's suppose that you're creating some custom login form (or overriding the default login form of FOSUserBundle) and you need to authenticate your user manually. For this, the first controller example would work only to set the user in the session, however it isn't authenticating really as we are not verifying neither the username nor the password.

The first we need to do before setting an user in the session, is to verify if the credentials he gave are correct. To do this we need the security encode service in our controller to check for the authenticity of the providen data. In this example we are going to show you with basic logic how the authentication process works:

<?php

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

class SomeController extends Controller
{
    public function loginAction(Request $request)
    {
        // This data is most likely to be retrieven from the Request object (from Form)
        // But to make it easy to understand ...
        $_username = "batman";
        $_password = "batmobil";

        // Retrieve the security encoder of symfony
        $factory = $this->get('security.encoder_factory');

        /// Start retrieve user
        // Let's retrieve the user by its username:
        // If you are using FOSUserBundle:
        $user_manager = $this->get('fos_user.user_manager');
        $user = $user_manager->findUserByUsername($_username);
        // Or by yourself
        $user = $this->getDoctrine()->getManager()->getRepository("userBundle:User")
                ->findOneBy(array('username' => $_username));
        /// End Retrieve user

        // Check if the user exists !
        if(!$user){
            return new Response(
                'Username doesnt exists',
                Response::HTTP_UNAUTHORIZED,
                array('Content-type' => 'application/json')
            );
        }

        /// Start verification
        $encoder = $factory->getEncoder($user);
        $salt = $user->getSalt();

        if(!$encoder->isPasswordValid($user->getPassword(), $_password, $salt)) {
            return new Response(
                'Username or Password not valid.',
                Response::HTTP_UNAUTHORIZED,
                array('Content-type' => 'application/json')
            );
        } 
        /// End Verification

        // The password matches ! then proceed to set the user in session
        
        //Handle getting or creating the user entity likely with a posted form
        // The third parameter "main" can change according to the name of your firewall in security.yml
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->get('security.token_storage')->setToken($token);

        // If the firewall name is not main, then the set value would be instead:
        // $this->get('session')->set('_security_XXXFIREWALLNAMEXXX', serialize($token));
        $this->get('session')->set('_security_main', serialize($token));
        
        // Fire the login event manually
        $event = new InteractiveLoginEvent($request, $token);
        $this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
        
        /*
         * Now the user is authenticated !!!! 
         * Do what you need to do now, like render a view, redirect to route etc.
         */
        return new Response(
            'Welcome '. $user->getUsername(),
            Response::HTTP_OK,
            array('Content-type' => 'application/json')
        );
    }
}

Happy coding !

Become a more social person