Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Datos QR y escaneo con una aplicación de autenticación

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 $12.00

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

Login Subscribe

Bien, comprobación de estado. Cualquier usuario puede activar la autenticación de dos factores en su cuenta haciendo clic en este enlace. Entre bastidores, cuando lo hacen, rellenamos el totpSecret en el objeto User, lo guardamos en la base de datos y, a continuación, mostramos un código QR que el usuario puede escanear. Este código QR es una imagen elegante que contiene dos datos. La primera es el correo electrónico de nuestro usuario. O, más concretamente, si me desplazo hasta los "métodos totp" en User, contiene lo que devolvemos degetTotpAuthenticationUsername():

... lines 1 - 20
class User implements UserInterface, PasswordAuthenticatedUserInterface, TwoFactorInterface
{
... lines 23 - 250
public function getTotpAuthenticationUsername(): string
{
return $this->getUserIdentifier();
}
... lines 255 - 266
}

Lo segundo que contiene la imagen del código QR es el totpSecret. En un minuto, voy a escanear este código con una aplicación de autenticación, que me permitirá generar el código correcto de autenticación de dos factores que necesitaré para iniciar la sesión. Lo hace aprovechando ese secreto.

Añadir información adicional al código QR

Pero primero, hay algo de información extra que podemos añadir al código QR. Dirígete a config/packages/scheb_2fa.yaml. En totp:, una de las cosas más importantes que puedes añadir se llama issuer. Voy a ponerlo en Cauldron Overflow:

# See the configuration reference at https://github.com/scheb/2fa/blob/master/doc/configuration.md
scheb_two_factor:
... lines 3 - 8
totp:
... line 10
issuer: 'Cauldron Overflow'

Eso, literalmente, acaba de añadir nueva información a la imagen del código QR. Observa la imagen cuando la actualizamos. ¿Lo ves? ¡Ha cambiado!

Gracias a esto, además de los email y totpSecret, el código contiene ahora una clave de "emisor". Si quieres conocer toda la información extra que puedes poner aquí, consulta la documentación o lee sobre la autenticación totp en general. Porque, por ejemplo, "emisor" no es más que un "concepto totp"... que ayuda a las apps autenticadoras a generar una etiqueta para nuestro sitio cuando escaneamos este código.

Escaneando con nuestra aplicación autenticadora

Llegados a este punto, quiero fingir que somos un usuario real y probar todo el flujo. Si fuéramos un usuario real, sacaríamos nuestro teléfono, abriríamos una app autentificadora -como Google authenticator o Authy- y escanearíamos este código.

A mí me gusta usar Authy, así es como se ve para mí. Agrego una nueva cuenta, escaneo y... ¡ya está! Lee mi correo electrónico y el "emisor" del código QR y sugiere un nombre y un logotipo. Si tu empresa es conocida, es posible que adivine el logotipo correcto, pero también puedes añadir un image a tu código QR de la misma manera que hemos añadido el "emisor". Cuando acepto esto, ¡me da los códigos!

Iniciando la sesión

¡Así que ya estamos listos! ¡Vamos a probarlo! Cierra la sesión... y vuelve a entrar conabraca_admin@example.com, contraseña tada. Envía y... ¡muy bien! En lugar de iniciar la sesión, se nos redirige a la página de autenticación de dos factores Esto ocurre por dos razones. En primer lugar, el usuario tiene activada la autenticación de dos factores en su cuenta. Concretamente, este método isTotpAuthenticationEnabled() devolvió true. En segundo lugar, el "token" de seguridad -esa cosa interna que envuelve a tu objetoUser cuando te conectas- coincide con uno de los tokens de nuestra configuración. En concreto, obtenemos el UsernamePasswordToken cuando nos conectamos a través del mecanismo form_login.

Si intentamos ir a cualquier otro lugar del sitio, nos devuelve aquí. El único lugar al que podemos ir es /logout si queremos cancelar el proceso. Esto se debe a que el paquete de dos factores ahora denegará el acceso a cualquier página de nuestro sitio a menos que lo hayas permitido explícitamente a través de las reglas de access_control, como hicimos para/logout y para la URL que muestra este formulario. Este formulario es feo, pero lo arreglaremos pronto.

Bien, volvamos a fingir que soy un usuario real. Abriré mi aplicación de autentificación, teclearé un código válido: 5, 3, 9, 9, 2, 2 y... ¡ya está! ¡Ya hemos iniciado la sesión! ¡Qué bien!

A continuación, vamos a personalizar ese formulario de autenticación de dos factores... porque era feo.

Leave a comment!

¡Este tutorial también funciona muy bien para Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.4.1 || ^8.0.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.3", // v3.3.0
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "doctrine/annotations": "^1.0", // 1.13.2
        "doctrine/doctrine-bundle": "^2.1", // 2.6.3
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.1.1
        "doctrine/orm": "^2.7", // 2.10.1
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "knplabs/knp-time-bundle": "^1.11", // v1.16.1
        "pagerfanta/doctrine-orm-adapter": "^3.3", // v3.3.0
        "pagerfanta/twig": "^3.3", // v3.3.0
        "phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
        "scheb/2fa-bundle": "^5.12", // v5.12.1
        "scheb/2fa-qr-code": "^5.12", // v5.12.1
        "scheb/2fa-totp": "^5.12", // v5.12.1
        "sensio/framework-extra-bundle": "^6.0", // v6.2.0
        "stof/doctrine-extensions-bundle": "^1.4", // v1.6.0
        "symfony/asset": "5.3.*", // v5.3.4
        "symfony/console": "5.3.*", // v5.3.7
        "symfony/dotenv": "5.3.*", // v5.3.8
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/form": "5.3.*", // v5.3.8
        "symfony/framework-bundle": "5.3.*", // v5.3.8
        "symfony/monolog-bundle": "^3.0", // v3.7.0
        "symfony/property-access": "5.3.*", // v5.3.8
        "symfony/property-info": "5.3.*", // v5.3.8
        "symfony/rate-limiter": "5.3.*", // v5.3.4
        "symfony/runtime": "5.3.*", // v5.3.4
        "symfony/security-bundle": "5.3.*", // v5.3.8
        "symfony/serializer": "5.3.*", // v5.3.8
        "symfony/stopwatch": "5.3.*", // v5.3.4
        "symfony/twig-bundle": "5.3.*", // v5.3.4
        "symfony/ux-chartjs": "^1.3", // v1.3.0
        "symfony/validator": "5.3.*", // v5.3.8
        "symfony/webpack-encore-bundle": "^1.7", // v1.12.0
        "symfony/yaml": "5.3.*", // v5.3.6
        "symfonycasts/verify-email-bundle": "^1.5", // v1.5.0
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.3
        "twig/string-extra": "^3.3", // v3.3.3
        "twig/twig": "^2.12|^3.0" // v3.3.3
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.0
        "symfony/debug-bundle": "5.3.*", // v5.3.4
        "symfony/maker-bundle": "^1.15", // v1.34.0
        "symfony/var-dumper": "5.3.*", // v5.3.8
        "symfony/web-profiler-bundle": "5.3.*", // v5.3.8
        "zenstruck/foundry": "^1.1" // v1.13.3
    }
}