Buscar en la documentación

Configurar la verificación en dos pasos

Escanear el QR, confirmar con el primer código y guardar los códigos de recuperación, ya sea durante el ingreso o desde Mi cuenta.

Módulo auth2 min de lecturaVer en GitHub

Todos los roles exigen la verificación en dos pasos (MFA), con un ingreso de gracia. Por eso se llega a esta pantalla de tres formas:

  • Sugerida: es el primer ingreso de la persona. Ya tiene la sesión abierta y puede elegir “Configurar más tarde”.
  • Obligatoria: ya usó el ingreso de gracia. Tiene un token a medias que solo sirve para configurar el MFA; si cancela, vuelve al login.
  • Voluntaria: desde Mi cuenta, con “Cancelar”.
Diagrama5 carriles · 15 pasos · 2 salidas de error
SíNoSíNoSíNoAbre/configurar-mfa1Envía POST/auth/mfa/setup2¿Ya tiene elMFA activo?3Responde 409Guarda un secretonuevo, cifrado ysin confirmar4Muestra el QR y laclave en grupos de45Escanea el QR yescribe el primercódigo6Envía POST/auth/mfa/setup/confirm7¿El códigocorresponde alsecreto?8Responde 422Activa el factor yguarda 8 códigos derecuperación9Aviso deverificaciónactivada10¿Venía de uningreso amedias?11Cambia el token amedias por unasesión12Muestra los 8códigos una solavez13Marca "Ya guardémis códigos"14Sigue a ?volver=,al inicio o a Micuenta15
Los números coinciden con el paso a paso. En pantallas pequeñas el diagrama se desliza de lado.

Paso a paso

  1. NavegadorAbre /configurar-mfa

    El modo sale del estado de la sesión: token a medias con mfa_setup (obligatoria), sesión abierta con ?sugerido=1 (sugerida) o sesión abierta sin él (voluntaria).

  2. NavegadorEnvía POST /auth/mfa/setup
  3. API¿Ya tiene el MFA activo?
  4. Base de datosGuarda un secreto nuevo, cifrado y sin confirmar

    El secreto se cifra con la clave de la aplicación (user_mfa_factors.secret, cast encrypted) y no vale hasta que se confirma. La API lo entrega solo en esta respuesta, junto con la dirección otpauth:// del QR.

  5. NavegadorMuestra el QR y la clave en grupos de 4

    El QR se dibuja en SVG desde la matriz de la librería qrcode, sin v-html. La clave en grupos de 4 sirve para escribirla a mano si la cámara no lee el QR.

  6. PersonaEscanea el QR y escribe el primer código

    Con cualquier aplicación de autenticación (Google Authenticator, Microsoft Authenticator, 1Password…). La cuenta aparece como "Trazo" y el correo.

  7. NavegadorEnvía POST /auth/mfa/setup/confirm

    Límite mfa: 5 por minuto por persona.

  8. API¿El código corresponde al secreto?
  9. Base de datosActiva el factor y guarda 8 códigos de recuperación

    Los códigos de recuperación se guardan solo como hash SHA-256 en user_recovery_codes. Se anota el paso de tiempo usado para que ese mismo código no sirva para entrar.

  10. CorreoAviso de verificación activada

    Va por la cola, cifrado mientras espera, en texto plano. Si no fue la persona, sabe que debe avisar.

  11. API¿Venía de un ingreso a medias?
  12. Base de datosCambia el token a medias por una sesión
  13. NavegadorMuestra los 8 códigos una sola vez

    Con "Copiar todos" y "Descargar .txt". Después no se pueden volver a ver; solo se pueden generar unos nuevos.

  14. PersonaMarca "Ya guardé mis códigos"
  15. NavegadorSigue a ?volver=, al inicio o a Mi cuenta

    Mi cuenta si llegó desde ahí; si no, la página de origen o el inicio.

Qué ve la persona en cada error

CódigoHTTPCuándoMensaje en pantalla
MFA_ALREADY_ENABLED409La cuenta ya tiene el MFA activo (por ejemplo, otra pestaña lo terminó)Tu cuenta ya tiene activa la verificación en dos pasos.
MFA_CODE_INVALID422El código no corresponde al secretoEl código no es correcto. Escribe el que muestra ahora tu aplicación de autenticación.
MFA_SETUP_NOT_STARTED409Se confirma sin haber pedido el secretoLa configuración no está en curso. Vuelve a empezarla desde el primer paso.
MFA_CHALLENGE_EXPIRED401En el modo obligatorio pasaron más de 5 minutos desde la contraseñaPasó demasiado tiempo desde que escribiste tu contraseña. Vuelve a ingresar para continuar.
TOO_MANY_REQUESTS429Más de 5 confirmaciones en un minutoHiciste demasiados intentos seguidos. Espera {wait} y vuelve a intentarlo.

Dónde está en el código