Creating a private area with authentication in Symfony 4.3
In 9 easy steps
Post by Pedro Resende on the 20 of June of 2019 at 14:12
Tags: symfony4.3authenticationsecurityloginformprivate
Today, I've decided to take a look at Symfony 4.3 and it's login system. I must say they've made a pretty good job with the new funcionalities and the ease of use. Therefor I've decided to share with you a small tutorial on how to add a login form and a private page in easy 9 steps.
I'll assume you've already installed Symfony 4.3 and Mysql configured, if you haven't please follow check how to do it here.
Let's start by running the following command in the terminal:
./bin/console make:user
you should answer the following questions
 The name of the security user class (e.g. User) [User]:
 > 
 Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]:
 > 
 Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]:
 > 
 Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).
 Does this app need to hash/check user passwords? (yes/no) [yes]:
 > 
The newer Argon2i password hasher requires PHP 7.2, libsodium or paragonie/sodium_compat. Your system DOES support this algorithm.
You should use Argon2i unless your production system will not support it.
 Use Argon2i as your password hasher (bcrypt will be used otherwise)? (yes/no) [yes]:
 > 
 created: src/Entity/User.php
 created: src/Repository/UserRepository.php
 updated: src/Entity/User.php
 updated: config/packages/security.yaml
           
  Success! 
Now, we have our entity created, please proceed with the migration creation and the creation of the User table in the database.
./bin/console make:migration && ./bin/console doctrine:migrations:migrate
Let's create a Guard Authenticator
./bin/console make:auth
Where you'll defined the desired options
 What style of authentication do you want? [Empty authenticator]:
  [0] Empty authenticator
  [1] Login form authenticator
 > 1
 The class name of the authenticator to create (e.g. AppCustomAuthenticator):
 > AppAuthenticator
 Choose a name for the controller class (e.g. SecurityController) [SecurityController]:
 > 
 created: src/Security/AppAuthenticator.php
 updated: config/packages/security.yaml
 created: src/Controller/SecurityController.php
 created: templates/security/login.html.twig
           
  Success! 
           
 Next:
 - Customize your new authenticator.
 - Finish the redirect "TODO" in the App\Security\AppAuthenticator::onAuthenticationSuccess() method.
 - Review & adapt the login template: templates/security/login.html.twig.
Now, we need a registration for to add the users to the database, we'll execute the following command for that
./bin/console make:registration-form
 Creating a registration form for App\Entity\User
 Do you want to add a @UniqueEntity validation annotation on your User class to make sure duplicate accounts aren't created? (yes/no) [yes]:
 > 
 Do you want to automatically authenticate the user after registration? (yes/no) [yes]:
 > 
 updated: src/Entity/User.php
 created: src/Form/RegistrationFormType.php
 created: src/Controller/RegistrationController.php
 created: templates/registration/register.html.twig
           
  Success! 
           
 Next: Go to /register to check out your new form!
 Make any changes you need to the form, controller & template.
Now, edit the file src/Controller/SecurityController.php and add the following
    /**
     * @Route("/logout", name="app_logout", methods={"GET"})
     */
    public function logout()
    {
        // controller can be blank: it will never be executed!
        throw new \Exception('Don\'t forget to activate logout in security.yaml');
    }
This will allow the users to logout.
Now, in the security.yml we need to define that the app_logout path is responsible for the users logout
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            logout:
                path:   app_logout
Let's update the login reddirect path by changing in the file, where you have
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }
        // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
        throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
    }
By
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }
        return new RedirectResponse($this->urlGenerator->generate('default'));
    }
Finally, create the following controller symfony/src/Controller/DefaultController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class DefaultController extends AbstractController
{
    /**
     * @Route("/", name="default")
     */
    public function index()
    {
        return $this->render('default/index.html.twig', [
            'controller_name' => 'DefaultController',
        ]);
    }
    /**
     * @Route("/private", name="private")
     */
    public function private()
    {
        $this->denyAccessUnlessGranted('ROLE_USER');
        return $this->render('default/index.html.twig', [
            'controller_name' => 'DefaultController',
        ]);
    }
}
Now, all you have to do is register a new user, by accessing to the path /register and to login, use the path /login.
P.S. - Please let me know if you have a easier and cleaner way, without bundles.
