logo cosasdedevs
Parte 6: Autenticación y registro con Symfony 6

Parte 6: Autenticación y registro con Symfony 6



My Profile
Mar 07, 2023

En el tutorial de esta semana vamos a aprender como generar automáticamente el sistema de registro y autenticación con Symfony 6. Gracias a las herramientas que incluye Symfony 6, este realizará la mayor parte del trabajo y nosotros solo tendremos que realizar unos pequeños ajustes para ponerlo a punto 💪.

👋 ¡Hola! Cuando empecé con esta serie de tutoriales sobre Symfony 6 no esperaba que se alargase más de 6 tutoriales, pero aquí estamos, justo en el sexto y creo que tengo para otros dos o tres más, bienvenidos, sean 🤣.

Esto significa que estamos profundizando bastante en el framework y esto me sirve para aprender más sobre él y ya de paso tu también 💪.

Antes de empezar 🛑 y como siempre, si te has perdido alguno de los tutoriales de esta serie, aquí te dejo todos los tutoriales escritos hasta ahora:

Generar sistema de autenticación con Symfony 6

Para empezar a trabajar con el sistema de login lo primero que debemos hacer es crear la entidad de usuarios con el comando php bin/console make:user, pero esto ya lo hicimos en la segunda parte de esta serie de tutoriales, así que primer paso hecho ✅.

Este paso solo te haría falta si no estás siguiendo la serie completa. Puedes ir a ese tutorial para entender mejor el funcionamiento de la entidad User.

Ahora vamos a utilizar un comando de Symfony que no habíamos visto hasta ahora, el cual nos generará la plantilla de login, actualizará la configuración de seguridad, creará la clase de autenticación y el controlador para realizar el login/logout. Vamos a la terminal y lanzamos el siguiente comando:

php bin/console make:auth

Como siempre, aparecerá el asistente, el cual nos hará tres preguntas en las que yo respondí lo siguiente:

  • En la primera pregunta "What style of authentication do you want?", seleccionamos [1] Login form authenticator.
  • En la siguiente pregunta "The class name of the authenticator to create (e.g. AppCustomAuthenticator)", yo lo voy a llamar BlogAuthenticator.
  • Después me pide que elija un nombre para El SecurityController, yo dejo el que viene por defecto.
  • En la siguiente pregunta "Do you want to generate a '/logout' URL?", seleccionamos yes.
  • Luego nos pregunta si queremos soporte para el remember me, seleccionamos yes.
  • Por último nos pregunta como debería funcionar el remember me y seleccionamos la primera opción.
 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):
 > BlogAuthenticator
 Choose a name for the controller class (e.g. SecurityController) [SecurityController]:
 >
 Do you want to generate a '/logout' URL? (yes/no) [yes]:
 >
 Do you want to support remember me? (yes/no) [yes]:
 >
 How should remember me be activated? [Activate when the user checks a box]:
  [0] Activate when the user checks a box
  [1] Always activate remember me
 > 0
 created: src/Security/BlogAuthenticator.php
 updated: config/packages/security.yaml
 created: src/Controller/SecurityController.php
 created: templates/security/login.html.twig

Ahora vamos a utilizar otro comando que nos servirá para generar todo el sistema de registro, así que volvemos a la terminal y ejecutamos el siguiente comando:

php bin/console make:registration-form

Se abrirá el asistente y damos las siguientes respuestas:

  • En "Do you want to add a @UniqueEntity validation annotation on your User class to make sure duplicate accounts aren't created?", indicamos yes, de esta forma evitaremos emails duplicados.
  • En "Do you want to send an email to verify the user's email address after registration?", ya que no vamos a configurar el sistema de envío de emails, le decimos que no.
  • En "Do you want to automatically authenticate the user after registration?", indicamos yes.
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 send an email to verify the user's email address after registration? (yes/no) [yes]:
 > no
 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

Como puedes ver, se ha editado la entidad de User. El cambio que ha realizado es que ha convertido el email en clave única para evitar emails duplicados.

Ahora podemos revisar src\Controller\RegistrationController.phpsrc\Controller\SecurityController.php y podrás ver que tenemos tanto los métodos como las rutas definidas para el registro, login y logout así que vamos a añadirlas también en nuestra barra de navegación.

Modificación de templates

Abrimos templates\base.html.twig y sustituimos la etiqueta HTML ul y su contenido por el siguiente:

<ul class="hidden sm:flex justify-between w-96">
    <li>
        <a href="{{ path('get_posts') }}">Inicio</a>
    </li>
    <li>
        <a href="http://">Etiquetas</a>
    </li>
    <li>
        <a href="http://">Acerca de mi</a>
    </li>
    {% if app.user %}
        <li>
            <a href="{{ path('app_logout') }}">Cerrar sesión</a> |
            <strong>{{ app.user.username }}</strong>
        </li>
    {% else %}
        <li>
            <a href="{{ path('app_login') }}">Login</a>
        </li>
        <li>
            <a href="{{ path('app_register') }}">Registro</a>
        </li>
    {% endif %}
</ul>

Cuando estamos autenticados, Symfony envía a la plantilla dentro de app.user la información del usuario, así que lo que hacemos es controlar si existe con una condición if. Si es así, añadimos al menú de navegación el link para cerrar sesión y el nombre del usuario. En el caso contrario mostramos los enlaces al login y al registro.

También he modificado una de las clases de la etiqueta ul (w-64 por w-96), ya que ahora que hay más elementos, quedan muy juntos si no ampliamos el tamaño.

Si pulsamos en cualquiera de las dos opciones (login y registro), podemos ver tanto el formulario de autenticación como de registro, pero ahora se ven un poco feos ¿No? Vamos a mejorar el formato y para ello primero abrimos el archivo assets\styles\app.css para añadir unas cuantas clases:

.button {
    @apply inline-block p-2 mx-1 bg-orange-300 text-white uppercase text-xs font-medium text-center;
}

.button:hover {
    @apply bg-orange-400;
}

.header-login-register {
    @apply text-xl font-medium py-2;
}

.form-blog {
    @apply text-center;
}

.form-blog div {
    @apply p-2;
}

.form-blog div input[type=text], 
.form-blog div input[type=password], 
.form-blog div input[type=email] {
    @apply py-1 px-2 border-slate-300 border rounded w-full;
}

.form-blog label, .form-blog input {
    @apply block text-left;
}

También vamos a modificar el archivo templates\base.html.twig para tener la opción de no mostrar el footer en la página de registro y login así que lo abrimos y añadimos la siguiente modificación:

{% block footer %}
<footer class="text-center text-white bg-teal-900 py-4">
    <ul class="md:flex justify-center">
        <li><a href="http://">Hola</a></li>
        <li><a href="http://">Privacidad</a></li>
        <li><a href="http://">Términos y condiciones</a></li>
        <li>&#174; {{'now'|date('Y')}} Mi blog con Symfony 6 y TailwindCSS 🧑‍💻</li>
    </ul>
</footer>
{% endblock %}

Aquí lo que hemos hecho es meter el footer en un block, de esta forma podremos modificarlo en las templates de login y registro para que no muestre nada. En las demás plantillas, al no declarar el bloque, mostrará este por defecto.

El siguiente paso es abrir la template templates\security\login.html.twig la cual contiene el formulario de autenticación y reemplazamos su contenido por el siguiente:

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

{% block title %}Iniciar sesión{% endblock %}

{% block body %}
<div class="mt-10 md:flex justify-center">
    <div class="max-w-full flex-row justify-center mb-8 md:mr-2">
        <form method="post">
            {% if error %}
                <div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
            {% endif %}

            {% if app.user %}
                <div class="mb-3">
                    You are logged in as {{ app.user.userIdentifier }}, <a href="{{ path('app_logout') }}">Logout</a>
                </div>
            {% endif %}

            <div class="form-blog">
                <h1 class="header-login-register">Entra en mi blog de Symfony</h1>
                <div>
                    <label for="inputEmail" class="required">Email:</label>
                    <input type="email" value="{{ last_username }}" name="email" id="inputEmail" autocomplete="email" required autofocus>
                </div>
                <div>
                    <label for="inputPassword" class="required">Contraseña:</label>
                    <input type="password" name="password" id="inputPassword" autocomplete="current-password" required>
                </div>

                <input type="hidden" name="_csrf_token"
                    value="{{ csrf_token('authenticate') }}"
                >

                <button class="button" type="submit">
                    Iniciar sesión
                </button>
            </div>
        </form>
    </div>
</div>
{% endblock %}
{% block footer %}{% endblock %}

Como puedes observar, los cambios en cuanto a la versión anterior solo son de estilos y añadir el bloque {% block footer %}{% endblock %} vacío para que no nos muestre el footer por defecto.

Por último, vamos al archivo templates\registration\register.html.twig y modificamos su contenido por el siguiente:

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

{% block title %}Regístrate en mi blog de Symfony{% endblock %}

{% block body %}
<div class="mt-10 md:flex justify-center">
    <div class="max-w-full flex-row justify-center mb-8 md:mr-2 text-center">
    <h1 class="header-login-register">Regístrate en mi blog de Symfony</h1>

    {{ form_start(registrationForm) }}

        <div class="form-blog">
            {{ form_row(registrationForm.email, {
                label: 'Email:'
            }) }}
            {{ form_row(registrationForm.username, {
                label: 'Nombre de usuario:'
            }) }}
            {{ form_row(registrationForm.plainPassword, {
                label: 'Contraseña:'
            }) }}
            {{ form_row(registrationForm.agreeTerms, {
                label: 'Aceptar términos:'
            }) }}

            <button type="submit" class="button">Registrarse</button>
        </div>
    {{ form_end(registrationForm) }}
    </div>
</div>
{% endblock %}
{% block footer %}{% endblock %}

Al igual que en el caso anterior, solo he añadido algunos estilos y he modificado el texto de las labels para que sea congruente con el de la plantilla de login.

Si ahora accedes a cualquiera de las dos páginas desde el navegador, verás que ahora tienen mucho mejor aspecto.

Modificaciones en BlogAuthenticator

Antes de empezar a probar nuestro sistema de login y registro, necesitamos realizar unos pequeños cambios. El primero será en el archivo src\Security\BlogAuthenticator.php, así que lo abrimos y modificamos el siguiente método:

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
    if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
        return new RedirectResponse($targetPath);
    }

    return new RedirectResponse($this->urlGenerator->generate('get_posts'));
}

Por defecto, Symfony añade una excepción en el método onAuthenticationSuccess para que nosotros sustituyamos esa excepción por la línea comentada y nombre del recurso al que queramos que apunte.

En nuestro caso, al hacer login quiero que se dirija a la página principal, por lo que indico el nombre que le dimos a la ruta (get_posts). De esta forma, cuando hagamos login siempre nos redirigirá a esa página.

Modificaciones en RegistrationController

Ahora mismo, si queremos crear un nuevo usuario, podemos ir a registro y crearlo sin problema. Lo único es que no añadiría automáticamente el "ROLE_USER" y nosotros queremos añadirlo, ya que lo necesitaremos más adelante, así que vamos al archivo src\Controller\RegistrationController.php y realizamos el siguiente cambio:

...
if ($form->isSubmitted() && $form->isValid()) {
    // encode the plain password
    $user->setPassword(
        $userPasswordHasher->hashPassword(
            $user,
            $form->get('plainPassword')->getData()
        )
    );

    $user->setRoles(['ROLE_USER']);

    $entityManager->persist($user);
    $entityManager->flush();
    // do anything else you need here, like send an email
...

En este caso, solo hemos añadido la línea $user->setRoles(['ROLE_USER']); que seteará el ROLE_USER cuando creemos un usuario.

Ahora solo queda que pruebes que funciona todo. Registra un usuario, autentícate con él. Revisa la base de datos y verás tu nuevo usuario con su password encriptado. Cualquier cosa escríbeme un comentario que estaré encantado de ayudarte 😉.

Con esto ya podemos dar por finalizado este tutorial. En el próximo profundizaremos más en el sistema de formularios y veremos como crear comentarios cuando estemos autenticados.

Como siempre os dejo el enlace al repo por si tenéis cualquier problema https://github.com/albertorc87/blog-symfony-tutorial/tree/autenticacion-registro-symfony-6

Espero que este post te ayude y como siempre, te recomiendo seguirme en Twitter para estar al tanto de los nuevo contenido. Ahora también puedes seguirme en Instagram donde estoy subiendo tips, tutoriales en vídeo e información sobre herramientas para developers.

Por último os dejo mi guía para aprender a trabajar con APIs donde explico todo el funcionamiento de una API, el protocolo HTTP y veremos como construir una API con arquitectura REST.

Nos leemos 👋.

3189 vistas

🐍 Sígueme en Twitter

Si te gusta el contenido que subo y no quieres perderte nada, sígueme en Twitter y te avisaré cada vez que cree contenido nuevo 💪
Luego ¡Te sigo!

Nos tomamos en serio tu privacidad

Utilizamos cookies propias y de terceros para recopilar y analizar datos sobre la interacción de los usuarios con cosasdedevs.com. Ver política de cookies.