Autentica con Firebase mediante un número de teléfono con JavaScript

Puedes usar Firebase Authentication para enviar un mensaje SMS al teléfono de un usuario para que este acceda. El usuario accede con un código único que se incluye en el mensaje SMS.

La forma más fácil de agregar un acceso con número de teléfono a la app es usar FirebaseUI, que incluye un widget de acceso directo que implementa flujos de acceso con número de teléfono, además de acceso federado y con contraseña. En este documento, se describe cómo implementar un flujo de acceso con el número de teléfono mediante el SDK de Firebase.

Antes de comenzar

Si aún no lo has hecho, copia el fragmento de inicialización de Firebase console en tu proyecto, como se describe en Agrega Firebase al proyecto de JavaScript.

Preocupaciones de seguridad

Si bien la autenticación con solo un número de teléfono es conveniente, es menos segura que otros métodos disponibles, ya que la posesión de un número de teléfono se puede transferir con facilidad entre usuarios. Además, en los dispositivos con varios perfiles de usuario, cualquier usuario que reciba mensajes SMS puede acceder a una cuenta con el número de teléfono del dispositivo.

Si usas el acceso con número de teléfono en la app, deberías ofrecerlo junto con métodos de acceso más seguros, además de informar a los usuarios acerca de las desventajas de usar el acceso con número de teléfono.

Habilita el acceso con número de teléfono para el proyecto de Firebase

Para que los usuarios accedan a través de SMS, primero debes habilitar el método de acceso con el número de teléfono para el proyecto de Firebase:

  1. En Firebase console, abre la sección Authentication.
  2. En la página de método de acceso, habilita el método de acceso Número de teléfono.
  3. En la misma página, si el dominio que alojará la app no aparece en la lista de la sección dominios de redireccionamiento de OAuth, agrega tu dominio. Ten en cuenta que localhost no se permite como dominio alojado para la autenticación telefónica.

Configura el verificador reCAPTCHA

Para poder habilitar el acceso de los usuarios con sus números de teléfono, debes configurar el verificador reCAPTCHA de Firebase. Firebase usa reCAPTCHA para evitar abusos. Por ejemplo, se asegura de que la solicitud de verificación del número de teléfono provenga de uno de los dominios permitidos de la app.

No es necesario que configures un cliente reCAPTCHA de forma manual; Firebase crea y maneja automáticamente todos los secretos y las claves necesarios del cliente cuando usas el objeto RecaptchaVerifier del SDK de Firebase.

El objeto RecaptchaVerifier admite reCAPTCHA invisible, que a menudo puede verificar al usuario sin necesidad de que este ejecute una acción, además del widget reCAPTCHA, que siempre solicita la interacción del usuario para completarse de forma correcta.

El reCAPTCHA subyacente que se muestra puede localizarse según la preferencia del usuario mediante la actualización del código de idioma en la instancia de Auth antes de procesar el reCAPTCHA. La localización mencionada también se aplicará al mensaje SMS que se envía al usuario y que contiene el código de verificación.

Web

import { getAuth } from "firebase/auth";

const auth = getAuth();
auth.languageCode = 'it';
// To apply the default browser preference instead of explicitly setting it.
// auth.useDeviceLanguage();

Web

firebase.auth().languageCode = 'it';
// To apply the default browser preference instead of explicitly setting it.
// firebase.auth().useDeviceLanguage();

Usa reCAPTCHA invisible

Para usar un reCAPTCHA invisible, crea un objeto RecaptchaVerifier con el parámetro size configurado como invisible y especifica el ID del botón que envía tu formulario de acceso. Por ejemplo:

Web

import { getAuth, RecaptchaVerifier } from "firebase/auth";

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'sign-in-button', {
  'size': 'invisible',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    onSignInSubmit();
  }
});

Web

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
  'size': 'invisible',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    onSignInSubmit();
  }
});

Usa el widget reCAPTCHA

Para usar el widget reCAPTCHA visible, crea un elemento en tu página a fin de que contenga el widget y, luego, crea un objeto RecaptchaVerifier y especifica el ID del contenedor cuando lo hagas. Por ejemplo:

Web

import { getAuth, RecaptchaVerifier } from "firebase/auth";

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {});

Web

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');

Opcional: Especifica los parámetros de reCAPTCHA

De forma opcional, puedes configurar funciones de devolución de llamada en el objeto RecaptchaVerifier a las que se llama cuando el usuario resuelve el reCAPTCHA o este vence antes de que el usuario envíe el formulario:

Web

import { getAuth, RecaptchaVerifier } from "firebase/auth";

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {
  'size': 'normal',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    // ...
  },
  'expired-callback': () => {
    // Response expired. Ask user to solve reCAPTCHA again.
    // ...
  }
});

Web

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container', {
  'size': 'normal',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    // ...
  },
  'expired-callback': () => {
    // Response expired. Ask user to solve reCAPTCHA again.
    // ...
  }
});

Opcional: Renderiza el reCAPTCHA por adelantado

Si quieres renderizar previamente el reCAPTCHA antes de enviar una solicitud de acceso, llama a render:

Web

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

Web

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

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

Web

const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);

Web

const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);

Envía un código de verificación al teléfono del usuario

Para iniciar el acceso con el número de teléfono, muéstrale al usuario una interfaz que le pida ingresar su número de teléfono y, luego, llama a signInWithPhoneNumber para solicitar a Firebase que envíe un código de autenticación al teléfono del usuario mediante SMS:

  1. Obtén el número de teléfono del usuario.

    Los requisitos legales varían, pero es recomendable establecer las expectativas de los usuarios informándoles que, si usan el acceso con el teléfono, es posible que reciban un mensaje SMS para la verificación y que se apliquen las tarifas estándar.

  2. Llama a signInWithPhoneNumber y pásale el número de teléfono del usuario y el RecaptchaVerifier que creaste anteriormente.

    Web

    import { getAuth, signInWithPhoneNumber } from "firebase/auth";
    
    const phoneNumber = getPhoneNumberFromUserInput();
    const appVerifier = window.recaptchaVerifier;
    
    const auth = getAuth();
    signInWithPhoneNumber(auth, phoneNumber, appVerifier)
        .then((confirmationResult) => {
          // SMS sent. Prompt user to type the code from the message, then sign the
          // user in with confirmationResult.confirm(code).
          window.confirmationResult = confirmationResult;
          // ...
        }).catch((error) => {
          // Error; SMS not sent
          // ...
        });

    Web

    const phoneNumber = getPhoneNumberFromUserInput();
    const appVerifier = window.recaptchaVerifier;
    firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
        .then((confirmationResult) => {
          // SMS sent. Prompt user to type the code from the message, then sign the
          // user in with confirmationResult.confirm(code).
          window.confirmationResult = confirmationResult;
          // ...
        }).catch((error) => {
          // Error; SMS not sent
          // ...
        });
    Si signInWithPhoneNumber da como resultado un error, restablece el reCAPTCHA para que el usuario pueda volver a intentarlo:
    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    window.recaptchaVerifier.render().then(function(widgetId) {
      grecaptcha.reset(widgetId);
    });

El método signInWithPhoneNumber envía el desafío de reCAPTCHA al usuario y, si el usuario lo pasa, solicita que Firebase Authentication envíe un SMS con un código de verificación al teléfono del usuario.

Permite el acceso del usuario con el código de verificación

Después de ejecutar correctamente la llamada a signInWithPhoneNumber, pide al usuario que ingrese el código de verificación que recibió por SMS. Luego, para que el usuario acceda, pasa el código al método confirm del objeto ConfirmationResult que se pasó al controlador de entrega de signInWithPhoneNumber (es decir, su bloque then). Por ejemplo:

Web

const code = getCodeFromUserInput();
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
});

Web

const code = getCodeFromUserInput();
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
});

Si la llamada a confirm se ejecuta como corresponde, el usuario accederá de forma correcta.

Obtén el objeto intermedio AuthCredential

Si necesitas obtener un objeto AuthCredential para la cuenta del usuario, pasa el código de verificación del resultado de la confirmación y el código de verificación a PhoneAuthProvider.credential en lugar de llamar a confirm:

var credential = firebase.auth.PhoneAuthProvider.credential(confirmationResult.verificationId, code);

Luego, puedes permitir el acceso del usuario con la credencial con esta línea de código:

firebase.auth().signInWithCredential(credential);

Realiza pruebas con números de teléfono ficticios

Puedes configurar números de teléfono ficticios para el desarrollo con Firebase console. Las pruebas con este tipo de números de teléfono brindan los siguientes beneficios:

  • Permiten probar la autenticación de números de teléfono sin consumir tu cuota de uso.
  • Permiten probar la autenticación de números de teléfono sin enviar SMS reales.
  • Ejecutan pruebas consecutivas con el mismo número de teléfono sin límites. Esto minimiza el riesgo de rechazo durante el proceso de revisión de la tienda de aplicaciones si el revisor utiliza el mismo número de teléfono para las pruebas.
  • Realizan pruebas con facilidad en entornos de desarrollo sin ningún esfuerzo adicional. Por ejemplo, puedes tener la capacidad de desarrollar en un simulador de iOS o un emulador de Android sin los Servicios de Google Play.
  • Permiten escribir pruebas de integración sin recibir bloqueos por controles de seguridad que se suelen aplicar a los números de teléfono reales en un entorno de producción.

Los números de teléfono ficticios deben cumplir los siguientes requisitos:

  1. Asegúrate de usar números de teléfono que sean ficticios y que aún no existan. Firebase Authentication no te permite establecer como números de prueba los teléfonos existentes que tengan usuarios reales. Una opción es usar números con el prefijo 555 como números de teléfono de prueba de EE.UU., por ejemplo: +1 650‑555‑3434.
  2. Los números de teléfono deben tener el formato adecuado de longitud y otras restricciones. Estos números pasarán por la misma validación que el número de teléfono de un usuario real.
  3. Puedes agregar hasta 10 números de teléfono.
  4. Usa códigos o números de teléfono de prueba que sean difíciles de adivinar y cámbialos con frecuencia.

Crea números de teléfono y códigos de verificación ficticios

  1. En la consola de Firebase, abre la sección Authentication.
  2. En la pestaña Método de acceso, habilita el proveedor de telefonía, si todavía no lo has hecho.
  3. Abre el menú de acordeón Números de teléfono para la prueba.
  4. Proporciona el número de teléfono que deseas probar, por ejemplo: +1 650-555-3434.
  5. Proporciona el código de verificación de 6 dígitos para ese número específico, por ejemplo: 654321.
  6. Agrega el número. Si es necesario, puedes desplazarte sobre la fila correspondiente y hacer clic en el ícono de papelera para borrar el número de teléfono y su código.

Realiza pruebas manuales

Puedes comenzar a usar un número de teléfono ficticio en tu aplicación directamente. Esto te permite realizar pruebas manuales durante las etapas de desarrollo sin que te encuentres con problemas de cuota o de límites. También puedes realizar pruebas directamente desde un simulador de iOS o un emulador de Android sin tener instalados los Servicios de Google Play.

Cuando proporcionas el número de teléfono ficticio y envías el código de verificación, no se envía ningún SMS real. En lugar de eso, debes proporcionar el código de verificación previamente configurado para completar el acceso.

Cuando se completa el acceso, se crea un usuario de Firebase con ese número de teléfono. El usuario tiene el mismo comportamiento y las mismas propiedades que el usuario de un número de teléfono real, y puede acceder de la misma manera a Realtime Database/Cloud Firestore y otros servicios. El token de ID emitido durante este proceso tiene la misma firma que el usuario de un número de teléfono real.

Otra opción es configurar una función de prueba mediante reclamaciones personalizadas en estos usuarios para diferenciarlos como usuarios falsos si se desea restringir más el acceso.

Pruebas de integración

Además de las pruebas manuales, Firebase Authentication proporciona APIs para ayudar a escribir pruebas de integración para las pruebas de autenticación telefónica. Estas API desactivan la verificación de la app cuando inhabilitan el requisito de reCAPTCHA en la Web y en las notificaciones push silenciosas en iOS. Esto hace que las pruebas de automatización sean posibles en estos flujos y más fáciles de implementar. Además, las API proporcionan la capacidad de probar flujos de verificación instantánea en Android.

En la Web, configura appVerificationDisabledForTesting como true antes de procesar el firebase.auth.RecaptchaVerifier. Esto resuelve el reCAPTCHA automáticamente, lo que te permite pasar el número de teléfono sin tener que resolverlo de manera manual. Ten en cuenta que, incluso si está inhabilitado reCAPTCHA, usar un número de teléfono real no completará el acceso. Con esta API, solo se pueden usar números de teléfono ficticios.

// Turn off phone auth app verification.
firebase.auth().settings.appVerificationDisabledForTesting = true;

var phoneNumber = "+16505554567";
var testVerificationCode = "123456";

// This will render a fake reCAPTCHA as appVerificationDisabledForTesting is true.
// This will resolve after rendering without app verification.
var appVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
// signInWithPhoneNumber will call appVerifier.verify() which will resolve with a fake
// reCAPTCHA response.
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
    .then(function (confirmationResult) {
      // confirmationResult can resolve with the fictional testVerificationCode above.
      return confirmationResult.confirm(testVerificationCode)
    }).catch(function (error) {
      // Error; SMS not sent
      // ...
    });

Los verificadores de apps para reCAPTCHA de prueba invisibles y visibles se comportan de manera diferente cuando está inhabilitada la verificación de apps:

  • reCAPTCHA visible: Cuando el reCAPTCHA visible se procesa a través de appVerifier.render(), se resuelve automáticamente después de una fracción de segundo. Esto equivale a que el usuario haga clic en el reCAPTCHA inmediatamente cuando se procesa. La respuesta de reCAPTCHA vencerá después de un tiempo, pero se resolverá nuevamente de manera automática.
  • reCAPTCHA invisible: El reCAPTCHA invisible no se resuelve automáticamente durante el procesamiento, sino cuando se produce la llamada a appVerifier.verify() o cuando se hace clic en el anclaje del botón de reCAPTCHA después de una fracción de segundo. Asimismo, la respuesta vencerá después de un tiempo y solo se resolverá automáticamente tras una llamada a appVerifier.verify() o cuando se haya hecho clic de nuevo en el anclaje del botón de reCAPTCHA.

La función de devolución de llamada correspondiente se activa con la respuesta falsa, tal como se esperaba, cada vez que se resuelve un reCAPTCHA de prueba. Si también se especifica la devolución de llamada de un vencimiento, se activará con el vencimiento.

Próximos pasos

Cuando un usuario accede por primera vez, se crea una cuenta de usuario nueva y se la vincula con las credenciales (el nombre de usuario y la contraseña, el número de teléfono o la información del proveedor de autenticación) que el usuario utilizó para acceder. Esta cuenta nueva se almacena como parte de tu proyecto de Firebase y se puede usar para identificar a un usuario en todas las apps del proyecto, sin importar cómo acceda.

  • En tus apps, para conocer el estado de autenticación del usuario, te recomendamos configurar un observador en el objeto Auth. Luego podrás obtener la información de perfil básica del usuario a partir del objeto User. Consulta Administra usuarios en Firebase.

  • En tus Reglas de seguridad de Firebase Realtime Database y Cloud Storage, puedes obtener el ID del usuario único que accedió a partir de la variable auth y usarlo para controlar a qué datos podrá acceder.

Para permitir que los usuarios accedan a tu app mediante varios proveedores de autenticación, puedes vincular las credenciales de estos proveedores con una cuenta de usuario existente.

Para salir de la sesión de un usuario, llama a signOut de la siguiente manera:

Web

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

Web

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});