Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Método personalizado del autentificador authenticate()

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Actualmente estamos convirtiendo nuestro antiguo autentificador de la Guardia en el nuevo sistema de autentificación. Y... estos dos sistemas comparten muchos métodos, como supports(),onAuthenticationSuccess() y onAuthenticationFailure().

La gran diferencia está en el nuevo método authenticate(). En el antiguo sistema Guard, dividimos la autenticación en varios métodos. Teníamos getCredentials(), en el que cogíamos alguna información, getUser(), en el que encontrábamos el objeto User, ycheckCredentials(), en el que comprobábamos la contraseña. Estas tres cosas se combinan ahora en el método authenticate()... con algunas bonificaciones. Por ejemplo, como verás en un segundo, ya no es nuestra responsabilidad comprobar la contraseña. Eso ahora ocurre automáticamente.

El objeto Pasaporte

Nuestro trabajo en authenticate() es sencillo: devolver un Passport. Sigue adelante y añade un tipo de retornoPassport. Esto es realmente necesario en Symfony 6. No se añadió automáticamente debido a una capa de desaprobación y al hecho de que el tipo de retorno cambió de PassportInterface a Passport en Symfony 5.4.

De todos modos, este método devuelve un Passport aquí... así que hazlo: return new Passport(). Por cierto, si eres nuevo en el nuevo sistema de autentificadores personalizados y quieres aprender más, echa un vistazo a nuestro tutorial de seguridad de Symfony 5 donde hablamos de todo esto. Ahora repasaré lo básico, pero los detalles están allí.

Antes de rellenar el Passport, coge toda la información del Request que necesitemos... pega... y luego establece cada una de ellas como variables:$email =, $password =... y preocupémonos del token CSRF más tarde.

El primer argumento del Passport() es un new UserBadge(). Lo que se pasa aquí es el identificador del usuario. ¡En nuestro sistema, nos registramos a través del correo electrónico, así que pasa$email!

Y... si quieres, puedes parar aquí. Si pasas sólo un argumento aUserBadge, Symfony utilizará tu "proveedor de usuario" de security.yaml para encontrar ese usuario. Nosotros estamos usando un proveedor entity, que le dice a Symfony que intente consultar el objeto User en la base de datos a través de la propiedad email.

Consulta personalizada opcional del usuario

En el sistema antiguo, hacíamos todo esto manualmente consultando el UserRepository. Eso ya no es necesario... pero a veces... si tienes una lógica personalizada, todavía querrás encontrar al usuario manualmente.

Si tienes este caso de uso, pasa un function() como segundo argumento que acepte un argumento $userIdentifier. Con esta configuración, cuando el sistema de autenticación necesite el objeto Usuario, llamará a nuestra función y nos pasará el "identificador de usuario"... que será lo que hayamos pasado al primer argumento. Es decir, el correo electrónico.

Nuestro trabajo es utilizarlo para devolver el usuario. Comienza con$user = $this->entityManager->getRepository(User::class)

Y sí, podría haber inyectado el UserRepository en lugar del gestor de entidades. Eso sería mejor... pero esto está bien. Luego ->findOneBy(['email' => $userIdentifier]).

Si no encontramos un usuario, necesitamos throw a new UserNotFoundException(). Luego, return $user.

¡El primer argumento Passport está hecho!

ContraseñaCredenciales

Para el segundo argumento, aquí abajo, cambia mi mal punto y coma por una coma - entonces dinew PasswordCredentials() y pasa esto el enviado $password.

Eso es todo lo que tenemos que hacer aquí. Sí, no necesitamos comprobar realmente la contraseña. Pasamos un PasswordCredentials()... y luego hay otro sistema que se encarga de comprobar la contraseña enviada con la contraseña con hash en la base de datos. ¿No es genial?

Insignias adicionales

Por último, el Passport acepta una matriz de "insignias", que son "cosas" extra que quieres añadir... normalmente para activar otras funciones.

Nosotros sólo necesitamos pasar uno: un new CsrfTokenBadge(). Esto se debe a que nuestro formulario de acceso está protegido por un token CSRF. Antes, lo comprobábamos manualmente. ¡Qué pena!

Pero ya no necesitamos hacerlo. Pasa la cadena authenticate al primer argumento... que sólo tiene que coincidir con la cadena utilizada cuando generamos el token en la plantilla: login.html.twig. Si busco csrf_token... ¡ahí está! utiliza la misma cadena que utilizamos aquí.

Para el segundo argumento, pasa el token CSRF enviado:$request->request->get('_csrf_token'), que también puedes ver en el formulario de acceso.

Y... ¡listo! Sólo con pasar la insignia, el token CSRF será validado.

Ah, y aunque no es necesario hacerlo, también voy a pasar unnew RememberMeBadge(). Si utilizas el sistema "Recuérdame", entonces sí necesitas pasar esta insignia. Indica al sistema que optas por tener un conjunto de fichas "Recuérdame". Para que funcione, tienes que tener una casilla de verificación "Recuérdame" o, para habilitarla siempre, añade ->enable() en la insignia.

Y, por supuesto, nada de esto funcionará a menos que actives el sistema remember_meen tu cortafuegos, lo cual aún no tengo. Sigue siendo seguro añadir esa placa... pero no habrá ningún sistema que la procese y añada la cookie.

¡Eliminación de métodos antiguos!

De todos modos, ¡hemos terminado! Si eso te pareció abrumador, retrocede y mira nuestro tutorial de seguridad de Symfony para obtener más contexto.

Lo bueno es que ya no necesitamos getCredentials(), getUser(),checkCredentials(), o getPassword(). Todo lo que necesitamos esauthenticate(), onAuthenticationSuccess(), onAuthenticationFailure(), ygetLoginUrl(). Incluso podemos celebrarlo aquí eliminando un montón de viejas declaraciones de uso. ¡Sí!

Ah, y mira el constructor aquí. Ya no necesitamos CsrfTokenManagerInterfaceni UserPasswordHasherInterface: ambas comprobaciones se hacen en otro lugar no por nosotros. Y eso nos da dos declaraciones de uso más para eliminar.

Activar el nuevo sistema de seguridad

Llegados a este punto, nuestro único autentificador personalizado se ha trasladado al nuevo sistema de autentificación. Esto significa que, en security.yaml, estamos preparados para cambiar al nuevo sistema. Digamos enable_authenticator_manager: true.

Y estos autentificadores personalizados ya no están bajo una clave guard. En su lugar, añade custom_authenticator y añade esto directamente debajo.

Bien, ¡el momento de la verdad! Acabamos de cambiar completamente al nuevo sistema. ¿Funcionará? Vuelve a la página de inicio, recarga y... ¡funciona! ¡Y fíjate en las depreciaciones! Hemos pasado de unas 45 a 4. ¡Woh!

Y algunas de ellas están relacionadas con otro cambio de seguridad. A continuación: actualicemos al nuevo password_hasher y comprobemos un nuevo comando para depurar los cortafuegos de seguridad.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^8.0.2",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.6", // v3.6.1
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.5
        "doctrine/annotations": "^1.13", // 1.13.2
        "doctrine/dbal": "^3.3", // 3.3.5
        "doctrine/doctrine-bundle": "^2.0", // 2.6.2
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.0", // 2.11.2
        "knplabs/knp-markdown-bundle": "^1.8", // 1.10.0
        "knplabs/knp-time-bundle": "^1.18", // v1.18.0
        "pagerfanta/doctrine-orm-adapter": "^3.6", // v3.6.1
        "pagerfanta/twig": "^3.6", // v3.6.1
        "sensio/framework-extra-bundle": "^6.0", // v6.2.6
        "sentry/sentry-symfony": "^4.0", // 4.2.8
        "stof/doctrine-extensions-bundle": "^1.5", // v1.7.0
        "symfony/asset": "6.0.*", // v6.0.7
        "symfony/console": "6.0.*", // v6.0.7
        "symfony/dotenv": "6.0.*", // v6.0.5
        "symfony/flex": "^2.1", // v2.1.7
        "symfony/form": "6.0.*", // v6.0.7
        "symfony/framework-bundle": "6.0.*", // v6.0.7
        "symfony/mailer": "6.0.*", // v6.0.5
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/property-access": "6.0.*", // v6.0.7
        "symfony/property-info": "6.0.*", // v6.0.7
        "symfony/proxy-manager-bridge": "6.0.*", // v6.0.6
        "symfony/routing": "6.0.*", // v6.0.5
        "symfony/runtime": "6.0.*", // v6.0.7
        "symfony/security-bundle": "6.0.*", // v6.0.5
        "symfony/serializer": "6.0.*", // v6.0.7
        "symfony/stopwatch": "6.0.*", // v6.0.5
        "symfony/twig-bundle": "6.0.*", // v6.0.3
        "symfony/ux-chartjs": "^2.0", // v2.1.0
        "symfony/validator": "6.0.*", // v6.0.7
        "symfony/webpack-encore-bundle": "^1.7", // v1.14.0
        "symfony/yaml": "6.0.*", // v6.0.3
        "symfonycasts/verify-email-bundle": "^1.7", // v1.10.0
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.8
        "twig/string-extra": "^3.3", // v3.3.5
        "twig/twig": "^2.12|^3.0" // v3.3.10
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.1
        "phpunit/phpunit": "^9.5", // 9.5.20
        "rector/rector": "^0.12.17", // 0.12.20
        "symfony/debug-bundle": "6.0.*", // v6.0.3
        "symfony/maker-bundle": "^1.15", // v1.38.0
        "symfony/var-dumper": "6.0.*", // v6.0.6
        "symfony/web-profiler-bundle": "6.0.*", // v6.0.6
        "zenstruck/foundry": "^1.16" // v1.18.0
    }
}