Symfony 4 / Sonata : Créer une authentification frontend

Pour utiliser une table différente de celle par défaut pour l’authentification,

Il faut d’abords créer notre entity qui va gérer nos utilisateurs.

Cette entity doit ensuite être implémenté à UserInterface.

Il faut donc ajouter un référence au composant et l’implémenter :

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Index;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Entity(repositoryClass="App\Repository\WcoconRepository")
 * @Table(name="wcocon")
 */
class Wcocon implements UserInterface
{

Maintenant que l’entity est un implément de userInterface, il faut ajouter les méthodes de l’implémentation.

    
    /*Authentication méthods implements interface User*/
    public function getRoles(){
        return ['ROLE_USER'];
    }
    

    public function getPassword(){
        return $this->getWcoPassword();
    }

    public function getSalt() {
        return false;
    }
    

    public function getUsername(){
        return $this->getWcoDossier();
    }
    

    public function eraseCredentials(){
        
    }
    

La fonction getRoles par défaut devra renvoyer un tableau des rôles. Ici ‘ROLE_USER’ mais on pourrais ajouter une logique plus complexe ici.

Ensuite il faut créer le controller qui va gérer notre authentification.
Via les commandes CLI il suffit de lancer : php bin/console make:controller
Et de créer notre SecurityController.

Sélection_038

Dans notre base.html.twig (/templates/base.html.twig), nous allons créer un block login, qui nous servira de placeholder pour notre formulaire

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.bundle.min.js" integrity="sha384-6khuMg9gaYr5AxOqhkVIODVIvm9ynTT5J4V1cfthmT+emCG6yVmEZsRHdxlotUnm" crossorigin="anonymous"></script>
        {% block stylesheets %}{% endblock %}
    </head>
    <body>
    
        <div class="container">
          <div class="row">
            <div class="col-sm-3">
                {% block login %}{% endblock %}
            </div>
            <div class="col-sm-9"> 
                {% block body %}{% endblock %}
            </div>
        
          </div>
        </div>    
        {% block javascripts %}{% endblock %}
    </body>
</html>

Et nous allons créer notre template de formulaire de login dans /var/www/cfc/delef/templates/security/login.html.twig.
Celui-ci étant une extention de notre base html et pour notre block login :

{% extends 'base.html.twig' %}

{% block login %}
	<h1>Connexion !</h1>
	<form action="{{ path('security_login') }}" method="post">
    	<div class="form-group">
    		<input placeholder="N° de dosser" 	required name="_username" type="text" 		class="form-control">
    	<div class="form-group">
    	</div>
    		<input placeholder="password" 		required name="_password" type="password" 	class="form-control">
    	</div>
    	<div class="form-group">
    		<button type="submit" class="btn btn-success">Connexion</button>
    	</div>
	</form>
{% endblock %}

A ce stade nous avons le rendu suivant :

Sélection_040

Attention, le formulaire de login doit contenir les champs _username et _password.

Il faut ensuite configurer l’encoder, puis le provider, et le firwall.
Notez que l’encoder pour du plain text est « plaintext ». il faudrait pour bien faire plutôt utiliser du bcrypt ou un autre sha.

security:
    encoders:
        App\Entity\Wcocon:
            algorithm: plaintext
    providers:
        frontend_users:
            entity:
              class: App\Entity\Wcocon
              property: wco_dossier
    firewalls:
        main:
            anonymous: true
            provider: frontend_users
            form_login:
                login_path: security_login
                check_path: security_login

Le fichier entier donne ceci :

security:
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt
        App\Entity\Wcocon:
            algorithm: plaintext
    providers:
        in_memory: { memory: null }
        fos_userbundle:
            id: fos_user.user_provider.username
        frontend_users:
            entity:
              class: App\Entity\Wcocon
              property: wco_dossier
        



    firewalls:
        # Disabling the security for the web debug toolbar, the profiler and Assetic.
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        # -> custom firewall for the admin area of the URL
        admin:
            pattern:            /admin(.*)
            context:            user
            form_login:
                provider:       fos_userbundle
                login_path:     /admin/login
                use_forward:    false
                check_path:     /admin/login_check
                failure_path:   null
            logout:
                path:           /admin/logout
                target:         /admin/login
            anonymous:          true

        # -> end custom configuration

        # default login area for standard users

        # This firewall is used to handle the public login area
        # This part is handled by the FOS User Bundle
        main:
            anonymous: true
            provider: frontend_users
            form_login:
                login_path: security_login
                check_path: security_login
            
    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        # Admin login page needs to be accessed without credential
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # Secured part of the site
        # This config requires being logged for the whole site and having the admin role for the admin part.
        # Change these rules to adapt them to your needs
        - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN, ROLE_SUPER_ADMIN] }
        - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }

Il nous faut ensuite configurer notre url de logout.
Pour cela, nous devons rajouter une route dans notre controller, et l’enregistrer dans la configuration.

Dans le controller ajouter la méthode :

    /**
     * @Route("/deconnexion", name="security_logout")
     */    
    public function logout(){}

Et dans la configuration :
Path fait référence à la route, et target permet de donner une url de redirection une fois l’authentification resetté.

        main:
            anonymous: true
            provider: frontend_users
            form_login:
                login_path: security_login
                check_path: security_login
            logout:
                path: security_logout
                target: /