Autenticar con Firebase usando un enlace de correo electrónico en JavaScript

Puede utilizar Firebase Authentication para iniciar sesión en un usuario enviándole un correo electrónico que contenga un enlace en el que puede hacer clic para iniciar sesión. En el proceso, también se verifica la dirección de correo electrónico del usuario.

Existen numerosos beneficios al iniciar sesión por correo electrónico:

  • Registro e inicio de sesión de baja fricción.
  • Menor riesgo de reutilización de contraseñas entre aplicaciones, lo que puede socavar la seguridad incluso de contraseñas bien seleccionadas.
  • La capacidad de autenticar a un usuario y al mismo tiempo verificar que el usuario es el propietario legítimo de una dirección de correo electrónico.
  • Un usuario solo necesita una cuenta de correo electrónico accesible para iniciar sesión. No se requiere propiedad de un número de teléfono o cuenta de redes sociales.
  • Un usuario puede iniciar sesión de forma segura sin necesidad de proporcionar (o recordar) una contraseña, lo que puede resultar engorroso en un dispositivo móvil.
  • Un usuario existente que haya iniciado sesión previamente con un identificador de correo electrónico (contraseña o federado) puede actualizarse para iniciar sesión solo con el correo electrónico. Por ejemplo, un usuario que ha olvidado su contraseña aún puede iniciar sesión sin necesidad de restablecerla.

Antes de que empieces

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

Para que los usuarios inicien sesión mediante un enlace de correo electrónico, primero debes habilitar el proveedor de correo electrónico y el método de inicio de sesión mediante enlace de correo electrónico para tu proyecto de Firebase:

  1. En Firebase console , abre la sección Auth .
  2. En la pestaña Método de inicio de sesión , habilite el proveedor de correo electrónico/contraseña . Tenga en cuenta que el inicio de sesión por correo electrónico/contraseña debe estar habilitado para utilizar el inicio de sesión mediante enlace de correo electrónico.
  3. En la misma sección, habilite el método de inicio de sesión mediante enlace de correo electrónico (inicio de sesión sin contraseña) .
  4. Clic en Guardar .

Para iniciar el flujo de autenticación, presente al usuario una interfaz que le solicite que proporcione su dirección de correo electrónico y luego llame a sendSignInLinkToEmail para solicitar que Firebase envíe el enlace de autenticación al correo electrónico del usuario.

  1. Construya el objeto ActionCodeSettings , que proporciona a Firebase instrucciones sobre cómo construir el enlace de correo electrónico. Establezca los siguientes campos:

    • url : el enlace profundo que se va a insertar y cualquier estado adicional que se va a transmitir. El dominio del enlace debe agregarse en la lista de dominios autorizados de Firebase Console, que se puede encontrar en la pestaña Método de inicio de sesión (Autenticación -> Configuración).
    • android e ios : las aplicaciones que se usarán cuando se abre el enlace de inicio de sesión en un dispositivo Android o Apple. Obtenga más información sobre cómo configurar Firebase Dynamic Links para abrir enlaces de acciones de correo electrónico a través de aplicaciones móviles.
    • handleCodeInApp : establecido en verdadero. La operación de inicio de sesión siempre debe completarse en la aplicación, a diferencia de otras acciones de correo electrónico fuera de banda (restablecimiento de contraseña y verificaciones de correo electrónico). Esto se debe a que, al final del flujo, se espera que el usuario inicie sesión y su estado de autenticación persista dentro de la aplicación.
    • dynamicLinkDomain : cuando se definen varios dominios de vínculos dinámicos personalizados para un proyecto, especifique cuál usar cuando el vínculo se abra a través de una aplicación móvil específica (por ejemplo, example.page.link ). De lo contrario, el primer dominio se selecciona automáticamente.

      Web modular API

      const actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be in the authorized domains list in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true.
        handleCodeInApp: true,
        iOS: {
          bundleId: 'com.example.ios'
        },
        android: {
          packageName: 'com.example.android',
          installApp: true,
          minimumVersion: '12'
        },
        dynamicLinkDomain: 'example.page.link'
      };

      Web namespaced API

      var actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be in the authorized domains list in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true.
        handleCodeInApp: true,
        iOS: {
          bundleId: 'com.example.ios'
        },
        android: {
          packageName: 'com.example.android',
          installApp: true,
          minimumVersion: '12'
        },
        dynamicLinkDomain: 'example.page.link'
      };

    Para obtener más información sobre ActionCodeSettings, consulte la sección Pasar estado en acciones de correo electrónico .

  2. Solicite al usuario su correo electrónico.

  3. Envíe el enlace de autenticación al correo electrónico del usuario y guarde el correo electrónico del usuario en caso de que complete el inicio de sesión de correo electrónico en el mismo dispositivo.

    Web modular API

    import { getAuth, sendSignInLinkToEmail } from "firebase/auth";
    
    const auth = getAuth();
    sendSignInLinkToEmail(auth, email, actionCodeSettings)
      .then(() => {
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        window.localStorage.setItem('emailForSignIn', email);
        // ...
      })
      .catch((error) => {
        const errorCode = error.code;
        const errorMessage = error.message;
        // ...
      });

    Web namespaced API

    firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings)
      .then(() => {
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        window.localStorage.setItem('emailForSignIn', email);
        // ...
      })
      .catch((error) => {
        var errorCode = error.code;
        var errorMessage = error.message;
        // ...
      });

Preocupaciones de seguridad

Para evitar que se utilice un enlace de inicio de sesión para iniciar sesión como un usuario no deseado o en un dispositivo no deseado, Firebase Auth requiere que se proporcione la dirección de correo electrónico del usuario al completar el flujo de inicio de sesión. Para que el inicio de sesión sea exitoso, esta dirección de correo electrónico debe coincidir con la dirección a la que se envió originalmente el enlace de inicio de sesión.

Puede optimizar este flujo para los usuarios que abren el enlace de inicio de sesión en el mismo dispositivo en el que solicitan el enlace, almacenando su dirección de correo electrónico localmente (por ejemplo, usando localStorage o cookies) cuando envía el correo electrónico de inicio de sesión. Luego, use esta dirección para completar el flujo. No pase el correo electrónico del usuario en los parámetros de la URL de redireccionamiento y reutilícelo, ya que esto puede permitir inyecciones de sesión.

Una vez completado el inicio de sesión, se eliminará del usuario cualquier mecanismo de inicio de sesión no verificado previo y se invalidarán todas las sesiones existentes. Por ejemplo, si alguien creó previamente una cuenta no verificada con el mismo correo electrónico y contraseña, la contraseña del usuario se eliminará para evitar que el imitador que reclamó la propiedad y creó esa cuenta no verificada inicie sesión nuevamente con el correo electrónico y la contraseña no verificados.

También asegúrese de utilizar una URL HTTPS en producción para evitar que su enlace sea potencialmente interceptado por servidores intermediarios.

Completar el inicio de sesión en una página web

El formato del enlace profundo del correo electrónico es el mismo que el formato utilizado para las acciones de correo electrónico fuera de banda (verificación de correo electrónico, restablecimiento de contraseña y revocación de cambio de correo electrónico). Firebase Auth simplifica esta verificación al proporcionar la API isSignInWithEmailLink para verificar si un enlace es un enlace de inicio de sesión con correo electrónico.

Para completar el inicio de sesión en la página de destino, llame a signInWithEmailLink con el correo electrónico del usuario y el enlace de correo electrónico real que contiene el código de un solo uso.

Web modular API

import { getAuth, isSignInWithEmailLink, signInWithEmailLink } from "firebase/auth";

// Confirm the link is a sign-in with email link.
const auth = getAuth();
if (isSignInWithEmailLink(auth, window.location.href)) {
  // Additional state parameters can also be passed via URL.
  // This can be used to continue the user's intended action before triggering
  // the sign-in operation.
  // Get the email if available. This should be available if the user completes
  // the flow on the same device where they started it.
  let email = window.localStorage.getItem('emailForSignIn');
  if (!email) {
    // User opened the link on a different device. To prevent session fixation
    // attacks, ask the user to provide the associated email again. For example:
    email = window.prompt('Please provide your email for confirmation');
  }
  // The client SDK will parse the code from the link for you.
  signInWithEmailLink(auth, email, window.location.href)
    .then((result) => {
      // Clear email from storage.
      window.localStorage.removeItem('emailForSignIn');
      // You can access the new user via result.user
      // Additional user info profile not available via:
      // result.additionalUserInfo.profile == null
      // You can check if the user is new or existing:
      // result.additionalUserInfo.isNewUser
    })
    .catch((error) => {
      // Some error occurred, you can inspect the code: error.code
      // Common errors could be invalid email and invalid or expired OTPs.
    });
}

Web namespaced API

// Confirm the link is a sign-in with email link.
if (firebase.auth().isSignInWithEmailLink(window.location.href)) {
  // Additional state parameters can also be passed via URL.
  // This can be used to continue the user's intended action before triggering
  // the sign-in operation.
  // Get the email if available. This should be available if the user completes
  // the flow on the same device where they started it.
  var email = window.localStorage.getItem('emailForSignIn');
  if (!email) {
    // User opened the link on a different device. To prevent session fixation
    // attacks, ask the user to provide the associated email again. For example:
    email = window.prompt('Please provide your email for confirmation');
  }
  // The client SDK will parse the code from the link for you.
  firebase.auth().signInWithEmailLink(email, window.location.href)
    .then((result) => {
      // Clear email from storage.
      window.localStorage.removeItem('emailForSignIn');
      // You can access the new user via result.user
      // Additional user info profile not available via:
      // result.additionalUserInfo.profile == null
      // You can check if the user is new or existing:
      // result.additionalUserInfo.isNewUser
    })
    .catch((error) => {
      // Some error occurred, you can inspect the code: error.code
      // Common errors could be invalid email and invalid or expired OTPs.
    });
}

Completar el inicio de sesión en una aplicación móvil

Firebase Authentication utiliza Firebase Dynamic Links para enviar el enlace de correo electrónico a un dispositivo móvil. Para completar el inicio de sesión a través de la aplicación móvil, la aplicación debe configurarse para detectar el enlace de la aplicación entrante, analizar el enlace profundo subyacente y luego completar el inicio de sesión como se hace a través del flujo web.

Para obtener más información sobre cómo gestionar el inicio de sesión con un enlace de correo electrónico en una aplicación de Android, consulte la guía de Android .

Para obtener más información sobre cómo manejar el inicio de sesión con un enlace de correo electrónico en una aplicación de Apple, consulte la guía de plataformas de Apple .

También puede vincular este método de autenticación a un usuario existente. Por ejemplo, un usuario previamente autenticado con otro proveedor, como un número de teléfono, puede agregar este método de inicio de sesión a su cuenta existente.

La diferencia estaría en la segunda mitad de la operación:

Web modular API

import { getAuth, linkWithCredential, EmailAuthProvider } from "firebase/auth";

// Construct the email link credential from the current URL.
const credential = EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Link the credential to the current user.
const auth = getAuth();
linkWithCredential(auth.currentUser, credential)
  .then((usercred) => {
    // The provider is now successfully linked.
    // The phone user can now sign in with their phone number or email.
  })
  .catch((error) => {
    // Some error occurred.
  });

Web namespaced API

// Construct the email link credential from the current URL.
var credential = firebase.auth.EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Link the credential to the current user.
firebase.auth().currentUser.linkWithCredential(credential)
  .then((usercred) => {
    // The provider is now successfully linked.
    // The phone user can now sign in with their phone number or email.
  })
  .catch((error) => {
    // Some error occurred.
  });

Esto también se puede utilizar para volver a autenticar a un usuario de enlace de correo electrónico antes de ejecutar una operación confidencial.

Web modular API

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

// Construct the email link credential from the current URL.
const credential = EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Re-authenticate the user with this credential.
const auth = getAuth();
reauthenticateWithCredential(auth.currentUser, credential)
  .then((usercred) => {
    // The user is now successfully re-authenticated and can execute sensitive
    // operations.
  })
  .catch((error) => {
    // Some error occurred.
  });

Web namespaced API

// Construct the email link credential from the current URL.
var credential = firebase.auth.EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Re-authenticate the user with this credential.
firebase.auth().currentUser.reauthenticateWithCredential(credential)
  .then((usercred) => {
    // The user is now successfully re-authenticated and can execute sensitive
    // operations.
  })
  .catch((error) => {
    // Some error occurred.
  });

Sin embargo, como el flujo podría terminar en un dispositivo diferente donde el usuario original no inició sesión, es posible que este flujo no se complete. En ese caso, se puede mostrar un error al usuario para obligarlo a abrir el enlace en el mismo dispositivo. Se puede pasar algún estado en el enlace para proporcionar información sobre el tipo de operación y el uid de usuario.

Si creó su proyecto a partir del 15 de septiembre de 2023, la protección de enumeración de correo electrónico está habilitada de forma predeterminada. Esta característica mejora la seguridad de las cuentas de usuario de su proyecto, pero deshabilita el método fetchSignInMethodsForEmail() , que recomendábamos anteriormente para implementar flujos de identificador primero.

Aunque puede desactivar la protección de enumeración de correo electrónico para su proyecto, le recomendamos que no lo haga.

Consulte la documentación sobre protección de enumeración de correo electrónico para obtener más detalles.

Plantilla de correo electrónico predeterminada para iniciar sesión mediante enlace

La plantilla de correo electrónico predeterminada incluye una marca de tiempo en el asunto y el cuerpo del correo electrónico para que los correos electrónicos posteriores no se colapsen en un solo hilo y el enlace quede oculto .

Esta plantilla se aplica a los siguientes idiomas:

Código Idioma
Arkansas Arábica
zh-CN Chino simplificado)
zh-TW Chino tradicional)
nl Holandés
es Inglés
es-ES Inglés del Reino Unido)
fr Francés
Delaware Alemán
identificación indonesio
él italiano
ja japonés
ko coreano
sustantivo, masculino, plural— Polaco
pt-BR Portugués (Brasil)
pt-PT Portugués (Portugal)
ru ruso
es Español
es-419 Español (Latinoamérica)
th tailandés

Próximos pasos

Después de que un usuario inicia sesión por primera vez, se crea una nueva cuenta de usuario y se vincula a las credenciales (es decir, el nombre de usuario y la contraseña, el número de teléfono o la información del proveedor de autenticación) con las que el usuario inició sesión. Esta nueva cuenta se almacena como parte de su proyecto de Firebase y se puede usar para identificar a un usuario en cada aplicación de su proyecto, independientemente de cómo inicie sesión el usuario.

  • En sus aplicaciones, la forma recomendada de conocer el estado de autenticación de su usuario es configurar un observador en el objeto Auth . Luego puede obtener la información básica del perfil del usuario desde el objeto User . Consulte Administrar usuarios .

  • En las reglas de seguridad de Firebase Realtime Database y Cloud Storage, puede obtener el ID de usuario único del usuario que inició sesión a partir de la variable auth y usarlo para controlar a qué datos puede acceder un usuario.

Puede permitir que los usuarios inicien sesión en su aplicación utilizando múltiples proveedores de autenticación vinculando las credenciales del proveedor de autenticación a una cuenta de usuario existente.

Para cerrar la sesión de un usuario, llame signOut :

Web modular API

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

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

Web namespaced API

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