Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Agregar autenticación de varios factores a una app web

En este artículo, se muestra cómo agregar una autenticación de varios factores por SMS a tu app web.

La autenticación de varios factores aumenta la seguridad de tu app. Si bien los atacantes a menudo hackean las contraseñas y las cuentas en plataformas sociales, interceptar un mensaje de texto es más difícil.

Antes de comenzar

  1. Habilita un proveedor que admita la autenticación de varios factores. Estos incluyen:

    • Correo electrónico y contraseña
    • Enviar vínculo por correo electrónico
    • Google
    • Facebook
    • Twitter
    • GitHub
    • Microsoft
    • Yahoo
    • LinkedIn
    • SAML
    • OIDC
  2. Asegúrate de que tu app verifique los correos electrónicos de los usuarios. La MFA requiere la verificación por correo electrónico. Esto evita que los actores maliciosos se registren en un servicio con un correo electrónico que no les pertenece y, luego, bloqueen al propietario real mediante el agregado de un segundo factor.

Usa instancias múltiples

Si habilitas la autenticación de varios factores para usar en un entorno de multiusuario, asegúrate de completar los siguientes pasos (además del resto de las instrucciones en este documento):

  • En Cloud Console, selecciona la instancia con la que deseas trabajar.

  • En tu código, configura el campo tenantId en la instancia Auth como el ID de tu instancia. Por ejemplo:

    firebase.auth().tenantId = 'myTenantId1';
    

Habilita la autenticación de varios factores

  1. Ve a la página MFA de Identity Platform en Cloud Console.
    Ir a la página MFA

  2. En el cuadro llamado Autenticación de varios factores basada en SMS, haz clic en Habilitar.

  3. Ingresa los números de teléfono con los que probarás tu app. Si bien es opcional, se recomienda registrar los números de teléfono de prueba para evitar los límites durante el desarrollo.

  4. Si aún no autorizaste el dominio de tu app, haz clic en Agregar dominio a la derecha para agregarlo a la lista de permisos.

  5. Haga clic en Save.

Elige un patrón de inscripción

Puedes elegir si tu app requiere una autenticación de varios factores, además de cómo y cuándo inscribir a tus usuarios. Algunos patrones comunes incluyen los siguientes:

  • Inscribir el segundo factor del usuario como parte del registro. Usa este método si tu aplicación 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. Es posible que las apps que quieran fomentar el proceso de autenticación de varios factores, pero que no lo requieran, prefieran 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.

Configura el verificador de reCAPTCHA

Antes de poder enviar códigos de SMS, debes configurar un verificador de reCAPTCHA. Identity Platform usa reCAPTCHA para impedir que las solicitudes de verificación de números de teléfono provengan de uno de los dominios permitidos de tu app con el fin de evitar el abuso.

No necesitas configurar manualmente un cliente de reCAPTCHA. El objeto RecaptchaVerifier del SDK del cliente crea e inicializa automáticamente las claves y los secretos necesarios del cliente.

Usa reCAPTCHA invisible

El objeto RecaptchaVerifier admite un reCAPTCHA invisible, que a menudo puede verificar al usuario sin necesidad de interacción. Para usar un reCAPTCHA invisible, crea un RecaptchaVerifier con el parámetro size configurado como invisible y especifica el ID del elemento de IU que inicia la inscripción de varios factores:

var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
 'size': 'invisible',
 'callback': function(response) {
   // reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
   onSolvedRecaptcha();
 }
});

Usa el widget de reCAPTCHA

Para usar el widget de un reCAPTCHA visible, crea un elemento HTML que contenga el widget y, luego, crea un objeto RecaptchaVerifier con el ID del contenedor de IU. De forma opcional, también puedes configurar devoluciones de llamada que se invoquen cuando el reCAPTCHA se resuelva o venza:

var recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
  'recaptcha-container',
  // Optional reCAPTCHA parameters.
  {
    'size': 'normal',
    'callback': function(response) {
      // reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
      // ...
      onSolvedRecaptcha();
    },
    'expired-callback': function() {
      // Response expired. Ask user to solve reCAPTCHA again.
      // ...
    }
  });

Procesamiento previo de reCAPTCHA

De manera opcional, puedes procesar previamente el reCAPTCHA antes de iniciar la inscripción de dos factores:

recaptchaVerifier.render()
  .then(function(widgetId) {
    window.recaptchaWidgetId = widgetId;
  });

Cuando render() se resuelve, obtienes el ID del widget de reCAPTCHA, que puedes usar para hacer llamadas a la API de reCAPTCHA de la siguiente manera:

var recaptchaResponse = grecaptcha.getResponse(window.recaptchaWidgetId);

Inscribe un segundo factor

Para inscribir un nuevo factor secundario para un usuario, haz lo siguiente:

  1. Vuelve a autenticar al usuario.

  2. Pídele al usuario que ingrese su número de teléfono.

  3. Inicializa el verificador de reCAPTCHA como se indicó en la sección anterior. Omite este paso si ya se configuró una instancia de RecaptchaVerifier:

    var recaptchaVerifier = new firebase.auth.RecaptchaVerifier(container);
    
  4. Obtén una sesión de varios factores para el usuario:

    user.multiFactor.getSession().then(function(multiFactorSession) {
      // ...
    })
    
  5. Inicializa un objeto PhoneInfoOptions con el número de teléfono del usuario y la sesión de varios factores:

    // Specify the phone number and pass the MFA session.
    var phoneInfoOptions = {
      phoneNumber: phoneNumber,
      session: multiFactorSession
    };
    
  6. Envía un mensaje de verificación al teléfono del usuario:

    var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
    // Send SMS verification code.
    return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
      .then(function(verificationId) {
        // verificationId will be needed for enrollment completion.
      })
    

    Si bien no es obligatorio, se recomienda informar con anticipación a los usuarios que recibirán un mensaje SMS y que se aplicarán las tarifas estándar.

  7. Si la solicitud falla, restablece el reCAPTCHA y, luego, repite el paso anterior para que el usuario pueda volver a intentarlo. Ten en cuenta que verifyPhoneNumber() restablecerá automáticamente el reCAPTCHA cuando surja un error, ya que los tokens de reCAPTCHA son solo para uso único.

    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    recaptchaVerifier.render()
      .then(function(widgetId) {
        grecaptcha.reset(widgetId);
      });
    
  8. Una vez que se envía el código SMS, pídele al usuario que verifique el código:

    // Ask user for the verification code.
    var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
    
  9. Inicializa un objeto MultiFactorAssertion con la PhoneAuthCredential:

    var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
    
  10. Completa la inscripción. De manera opcional, puedes especificar un nombre visible para el segundo factor. Esto es útil para usuarios con varios segundos factores, ya que el número de teléfono se enmascara durante el flujo de autenticación (por ejemplo, +1****1234).

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(multiFactorAssertion, 'My personal phone number');
    

En el siguiente código, se muestra un ejemplo completo de la inscripción de un segundo factor:

var recaptchaVerifier = new firebase.auth.RecaptchaVerifier(container);
user.multiFactor.getSession().then(function(multiFactorSession) {
  // Specify the phone number and pass the MFA session.
  var phoneInfoOptions = {
    phoneNumber: phoneNumber,
    session: multiFactorSession
  };
  var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
  // Send SMS verification code.
  return phoneAuthProvider.verifyPhoneNumber(
      phoneInfoOptions, recaptchaVerifier);
})
.then(function(verificationId) {
  // Ask user for the verification code.
  var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
  var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
  // Complete enrollment.
  return user.multiFactor.enroll(multiFactorAssertion, mfaDisplayName);
});

Felicitaciones Registraste correctamente un segundo factor de autenticación para un usuario.

Permite que los usuarios accedan con un segundo factor

Para que un usuario acceda con la verificación por SMS de dos factores, haz lo siguiente:

  1. Haz que el usuario acceda con el primer factor y, luego, detecta el error auth/multi-factor-auth-required. Este error contiene un agente de resolución, sugerencias sobre los segundos factores inscritos y una sesión subyacente, si el usuario se autenticó correctamente con el primer factor.

    Por ejemplo, si el primer factor del usuario era un correo electrónico y una contraseña, haz lo siguiente:

    firebase.auth().signInWithEmailAndPassword(email, password)
      .then(function(userCredential) {
        // User successfully signed in and is not enrolled with a second factor.
      })
      .catch(function(error) {
        if (error.code == 'auth/multi-factor-auth-required') {
          // The user is a multi-factor user. Second factor challenge is required.
          resolver = error.resolver;
          // ...
        } else if (error.code == 'auth/wrong-password') {
          // Handle other errors such as wrong password.
        } ...
      });
    

    Si el primer factor del usuario es un proveedor federado, como OAuth, OIDC o SAML, detecta el error después de llamar a signInWithPopup() o signInWithRedirect().

  2. Si el usuario tiene varios factores secundarios inscritos, pregúntale cuál usar:

    // Ask user which second factor to use.
    // You can get the masked phone number via resolver.hints[selectedIndex].phoneNumber
    // You can get the display name via resolver.hints[selectedIndex].displayName
    if (resolver.hints[selectedIndex].factorId === firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) {
      // User selected a phone second factor.
      // ...
    } else {
      // Unsupported second factor.
      // Note that only phone second factors are currently supported.
    }
    
  3. Inicializa el verificador de reCAPTCHA como se indicó en la sección anterior. Omite este paso si ya se configuró una instancia de RecaptchaVerifier:

    var recaptchaVerifier = new firebase.auth.RecaptchaVerifier(container);
    
  4. Inicializa un objeto PhoneInfoOptions con el número de teléfono del usuario y la sesión de varios factores. Estos valores se encuentran en el objeto resolver que se pasa al error auth/multi-factor-auth-required:

    var phoneInfoOptions = {
      multiFactorHint: resolver.hints[selectedIndex],
      session: resolver.session
    };
    
  5. Envía un mensaje de verificación al teléfono del usuario:

    var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
    // Send SMS verification code.
    return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
      .then(function(verificationId) {
        // verificationId will be needed for sign-in completion.
      })
    
  6. Si la solicitud falla, restablece el reCAPTCHA y, luego, repite el paso anterior para que el usuario pueda volver a intentarlo:

    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    recaptchaVerifier.render()
      .then(function(widgetId) {
        grecaptcha.reset(widgetId);
      });
    
  7. Una vez que se envía el código SMS, pídele al usuario que verifique el código:

    // Ask user for the verification code.
    var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
    
  8. Inicializa un objeto MultiFactorAssertion con la PhoneAuthCredential:

    var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
    
  9. Llama a resolver.resolverSignIn() para completar la autenticación secundaria. Luego, puedes acceder al resultado del acceso original, en el que se incluyen los datos específicos del proveedor y las credenciales de autenticación estándar:

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(multiFactorAssertion)
      .then(function(userCredential) {
        // userCredential will also contain the user, additionalUserInfo, optional
        // credential (null for email/password) associated with the first factor sign-in.
        // For example, if the user signed in with Google as a first factor,
        // userCredential.additionalUserInfo will contain data related to Google provider that
        // the user signed in with.
        // user.credential contains the Google OAuth credential.
        // user.credential.accessToken contains the Google OAuth access token.
        // user.credential.idToken contains the Google OAuth ID token.
      });
    

En el siguiente código, se muestra un ejemplo completo de acceso de un usuario de varios factores:

var resolver;
firebase.auth().signInWithEmailAndPassword(email, password)
  .then(function(userCredential) {
    // User is not enrolled with a second factor and is successfully signed in.
    // ...
  })
  .catch(function(error) {
    if (error.code == 'auth/multi-factor-auth-required') {
      resolver = error.resolver;
      // Ask user which second factor to use.
      if (resolver.hints[selectedIndex].factorId ===
          firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) {
        var phoneInfoOptions = {
          multiFactorHint: resolver.hints[selectedIndex],
          session: resolver.session
        };
        var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
        // Send SMS verification code
        return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
          .then(function(verificationId) {
            // Ask user for the SMS verification code.
            var cred = firebase.auth.PhoneAuthProvider.credential(
                verificationId, verificationCode);
            var multiFactorAssertion =
                firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
            // Complete sign-in.
            return resolver.resolveSignIn(multiFactorAssertion)
          })
          .then(function(userCredential) {
            // User successfully signed in with the second factor phone number.
          });
      } else {
        // Unsupported second factor.
      }
    } else if (error.code == 'auth/wrong-password') {
      // Handle other errors such as wrong password.
    } ...
  });

Felicitaciones Accediste correctamente a un usuario mediante la autenticación de varios factores.

Próximos pasos