Symfony 4 / Sonata: Crear una autenticación de frontend

Para usar una tabla diferente a la predeterminada para la autenticación,

Primero necesitamos crear nuestra entidad que gestionará nuestros usuarios.

Esta entidad debe implementarse en UserInterface.

Entonces necesitamos añadir una referencia al componente e implementarlo:

<?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
{

Ahora que la entidad es una implementación de UserInterface, necesitamos agregar los métodos de implementación.


    /*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 función getDefaultRoles debería devolver un arreglo de roles. Aquí 'ROLE_USER', pero podríamos agregar lógica más compleja aquí.

A continuación, necesitamos crear el controlador que manejará nuestra autenticación.
Usando comandos de CLI, simplemente ejecuta: php bin/console make:controller
Y crea nuestro SecurityController.

Sélection_038

En nuestro base.html.twig (/templates/base.html.twig), crearemos un bloque de inicio de sesión, que servirá como contenedor para nuestro formulario

<!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>

Y crearemos nuestra plantilla de formulario de inicio de sesión en /var/www/cfc/delef/templates/security/login.html.twig.
Esta será una extensión de nuestro html base y para nuestro bloque de inicio de sesión:

{% 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 esta etapa, tenemos el siguiente renderizado:

Sélection_040

Ten cuidado, el formulario de inicio de sesión debe contener los campos _username y _password.

Luego necesitamos configurar el codificador, luego el proveedor y el firewall.
Ten en cuenta que el codificador para texto plano es "plaintext". Sería mejor usar bcrypt u otro 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

Todo el archivo se ve así:

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 }

Luego necesitamos configurar nuestra URL de cierre de sesión.
Para esto, debemos agregar una ruta en nuestro controlador y registrarla en la configuración.

En el controlador añade el método:

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

Y en la configuración:
Path se refiere a la ruta, y target permite dar una URL de redireccionamiento una vez que se restablece la autenticación.

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