Agrega la autenticación de varios factores TOTP a tu app web

Si actualizaste a Firebase Authentication con Identity Platform, puedes agregar la autenticación de varios factores (MFA) de contraseña de un solo uso basada en el tiempo (TOTP) a tu app.

Firebase Authentication con Identity Platform te permite usar una TOTP como factor adicional para la MFA. Cuando habilitas esta función, los usuarios que intentan acceder a tu app ven una solicitud de una TOTP. Para generarla, deben usar una app de autenticador capaz de generar códigos TOTP válidos, como Google Authenticator.

Antes de comenzar

  1. Habilita al menos un proveedor que admita la MFA. Ten en cuenta que todos los proveedores, excepto los siguientes, admiten la MFA:

    • Autenticación telefónica
    • Autenticación anónima
    • Tokens de autenticación personalizados
    • Apple Game Center
  2. Asegúrate de que tu app verifique las direcciones de correo electrónico de los usuarios. La MFA requiere verificación por correo electrónico. Esto evita que los actores maliciosos se registren en un servicio con una dirección de correo electrónico que no les pertenece y, luego, bloqueen al propietario real de la dirección de correo electrónico agregando un segundo factor.

  3. Si aún no lo has hecho, instala el SDK de Firebase JavaScript.

    La MFA de TOTP solo es compatible con las versiones 9.19.1 y posteriores del SDK web modular.

  4. Si aún no lo has hecho, instala el SDK de Firebase Admin.

    La MFA de TOTP solo es compatible con las versiones 11.6.0 y posteriores del SDK de Firebase Admin.

Habilitar MFA de TOTP

Para habilitar TOTP como el segundo factor, usa el SDK de Admin o llama al extremo de REST de configuración del proyecto.

Para usar el SDK de Admin, ejecuta lo siguiente:

import { getAuth } from 'firebase-admin/auth';

getAuth().projectConfigManager().updateProjectConfig(
{
      multiFactorConfig: {
          providerConfigs: [{
              state: "ENABLED",
              totpProviderConfig: {
                  adjacentIntervals: {
                      NUM_ADJ_INTERVALS
                  },
              }
          }]
      }
})

Reemplaza lo siguiente:

  • NUM_ADJ_INTERVALS: Es la cantidad de intervalos de tiempo, de cero a diez. El valor predeterminado es cinco.

Para habilitar la MFA de TOTP mediante la API de REST, ejecuta lo siguiente:

curl -X PATCH "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=mfa" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json" \
    -H "X-Goog-User-Project: PROJECT_ID" \
    -d \
    '{
        "mfa": {
          "providerConfigs": [{
            "state": "ENABLED",
            "totpProviderConfig": {
              "adjacentIntervals": "NUM_ADJ_INTERVALS"
            }
          }]
       }
    }'

Reemplaza lo siguiente:

  • PROJECT_ID: El ID del proyecto
  • NUM_ADJ_INTERVALS: Es la cantidad de intervalos de tiempo, de cero a diez. El valor predeterminado es cinco.

Elige un patrón de inscripción

Puedes elegir si tu app requerirá una autenticación de varios factores, además de cómo y cuándo inscribir a tus usuarios. Estos son algunos patrones comunes:

  • Inscribir el segundo factor del usuario como parte del registro. Usa este método si tu app requiere la autenticación de varios factores para todos los usuarios.

  • Ofrecer una opción que se puede omitir para inscribir un segundo factor durante el registro. Si quieres fomentar el proceso de autenticación de varios factores en tu app, pero no exigirlo, puedes usar este enfoque.

  • Proporcionar la capacidad de agregar un segundo factor desde la página de administración de la cuenta o el perfil del usuario, en lugar de la pantalla de registro. Esto minimiza la fricción durante el proceso de registro y, a la vez, permite que la autenticación de varios factores esté disponible para los usuarios sensibles a la seguridad.

  • Requiere agregar un segundo factor de manera incremental cuando el usuario quiera acceder a las funciones con requisitos de seguridad mayores.

Inscribir a los usuarios en la MFA de TOTP

Después de habilitar la MFA de TOTP como segundo factor para tu app, implementa la lógica del cliente para inscribir a los usuarios en la MFA de TOTP:

  1. Importa las clases y las funciones de MFA necesarias:

    import {
      multiFactor,
      TotpMultiFactorGenerator,
      TotpSecret,
      getAuth,
    } from "firebase/auth";
    
  2. Vuelve a autenticar al usuario.

  3. Genera un Secret de TOTP para el usuario autenticado:

    // Generate a TOTP secret.
    const multiFactorSession = await multiFactor(currentUser).getSession();
    const totpSecret = await TotpMultiFactorGenerator.generateSecret(
      multiFactorSession
    );
    
  4. Muestra el Secret al usuario y pídele que lo ingrese en su app de autenticador.

    Con muchas apps de autenticación, los usuarios pueden agregar rápidamente nuevos secretos de TOTP escaneando un código QR que represente un URI de clave compatible con el Autenticador de Google. Para generar un código QR con este fin, genera el URI con generateQrCodeUrl() y, luego, codifícalo con la biblioteca de código QR que elijas. Por ejemplo:

    const totpUri = totpSecret.generateQrCodeUrl(
        currentUser.email,
        "Your App's Name"
    );
    await QRExampleLib.toCanvas(totpUri, qrElement);
    

    Independientemente de si muestras un código QR, siempre muestra la clave secreta para admitir apps de autenticador que no puedan leer códigos QR:

    // Also display this key:
    const secret = totpSecret.secretKey;
    

    Después de que el usuario agrega su secreto a la app de autenticador, comenzará a generar TOTP.

  5. Pídele al usuario que escriba la TOTP que aparece en su app de autenticador y úsala para finalizar la inscripción de la MFA:

    // Ask the user for a verification code from the authenticator app.
    const verificationCode = // Code from user input.
    
    // Finalize the enrollment.
    const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment(
      totpSecret,
      verificationCode
    );
    await multiFactor(currentUser).enroll(multiFactorAssertion, mfaDisplayName);
    

Permite que los usuarios accedan con un segundo factor

Para que los usuarios accedan con la MFA de TOTP, usa el siguiente código:

  1. Importa las clases y las funciones de MFA necesarias:

    import {
        getAuth,
        getMultiFactorResolver,
        TotpMultiFactorGenerator,
    } from "firebase/auth";
    
  2. Llama a uno de los métodos signInWith como lo harías si no usaras la MFA. Por ejemplo, signInWithEmailAndPassword(). Si el método muestra un error auth/multi-factor-auth-required, inicia el flujo de MFA de tu app.

    try {
        const userCredential = await signInWithEmailAndPassword(
            getAuth(),
            email,
            password
        );
        // If the user is not enrolled with a second factor and provided valid
        // credentials, sign-in succeeds.
    
        // (If your app requires MFA, this could be considered an error
        // condition, which you would resolve by forcing the user to enroll a
        // second factor.)
    
        // ...
    } catch (error) {
        switch (error.code) {
            case "auth/multi-factor-auth-required":
                // Initiate your second factor sign-in flow. (See next step.)
                // ...
                break;
            case ...:  // Handle other errors, such as wrong passwords.
                break;
        }
    }
    
  3. El flujo de MFA de tu app primero debe solicitarle al usuario que elija el segundo factor que quiere usar. Si quieres obtener una lista de los segundos factores admitidos, examina la propiedad hints de una instancia MultiFactorResolver:

    const mfaResolver = getMultiFactorResolver(getAuth(), error);
    const enrolledFactors = mfaResolver.hints.map(info => info.displayName);
    
  4. Si el usuario elige usar TOTP, pídele que escriba la TOTP que aparece en su app de autenticador y que lo use para acceder:

    switch (mfaResolver.hints[selectedIndex].factorId) {
        case TotpMultiFactorGenerator.FACTOR_ID:
            const otpFromAuthenticator = // OTP typed by the user.
            const multiFactorAssertion =
                TotpMultiFactorGenerator.assertionForSignIn(
                    mfaResolver.hints[selectedIndex].uid,
                    otpFromAuthenticator
                );
            try {
                const userCredential = await mfaResolver.resolveSignIn(
                    multiFactorAssertion
                );
                // Successfully signed in!
            } catch (error) {
                // Invalid or expired OTP.
            }
            break;
        case PhoneMultiFactorGenerator.FACTOR_ID:
            // Handle SMS second factor.
            break;
        default:
            // Unsupported second factor?
            break;
    }
    

Dar de baja la MFA de TOTP

En esta sección, se describe cómo controlar la cancelación de la inscripción de un usuario a la MFA de TOTP.

Si un usuario se registró para usar varias opciones de MFA, y si da de baja la opción que se habilitó más recientemente, recibirá un auth/user-token-expired y saldrá de la cuenta. El usuario debe volver a acceder y verificar sus credenciales existentes, por ejemplo, una dirección de correo electrónico y una contraseña.

Para dar de baja al usuario, manejar el error y activar la reautenticación, usa el siguiente código:

import {
    EmailAuthProvider,
    TotpMultiFactorGenerator,
    getAuth,
    multiFactor,
    reauthenticateWithCredential,
} from "firebase/auth";

try {
    // Unenroll from TOTP MFA.
    await multiFactor(currentUser).unenroll(TotpMultiFactorGenerator.FACTOR_ID);
} catch  (error) {
    if (error.code === 'auth/user-token-expired') {
        // If the user was signed out, re-authenticate them.

        // For example, if they signed in with a password, prompt them to
        // provide it again, then call `reauthenticateWithCredential()` as shown
        // below.

        const credential = EmailAuthProvider.credential(email, password);
        await reauthenticateWithCredential(
            currentUser,
            credential
        );
    }
}

Próximos pasos