Autentica con Firebase mediante vínculos de correo electrónico

Puedes usar Firebase Authentication para que los usuarios accedan mediante el envío de un correo electrónico con un vínculo en el que se pueda hacer clic. Además, en el proceso se verifica la dirección de correo electrónico del usuario.

Existen numerosos beneficios asociados al acceso por correo electrónico:

  • Es sencillo registrarse y acceder.
  • Hay un menor riesgo de reutilización de contraseñas entre aplicaciones, lo que puede comprometer incluso la seguridad de contraseñas bien seleccionadas.
  • Se puede autenticar un usuario y, al mismo tiempo, verificar que sea el dueño legítimo de la dirección de correo electrónico.
  • El usuario solo necesita una cuenta de correo electrónico con acceso para ingresar. No es necesario poseer un número de teléfono ni una cuenta en redes sociales.
  • El usuario puede acceder sin necesidad de proporcionar (o recordar) una contraseña, lo que resulta especialmente práctico si se utiliza un dispositivo móvil.
  • Un usuario existente que antes accedió con un identificador de correo electrónico (contraseña o federado) puede actualizar la configuración para acceder solamente con el correo electrónico. Por ejemplo, un usuario que olvidó su contraseña puede acceder de igual modo sin necesidad de restablecerla.

Antes de comenzar

  1. Si aún no lo hiciste, sigue los pasos de la guía Cómo comenzar.

  2. Habilita el acceso mediante un vínculo de correo electrónico para tu proyecto de Firebase.

    Para que los usuarios accedan a través de un vínculo de correo electrónico, primero debes habilitar el método de acceso con proveedor y vínculo de correo electrónico para el proyecto de Firebase:

    1. En Firebase console, abre la sección Auth.
    2. En la pestaña Método de acceso, habilita el proveedor de Correo electrónico/Contraseña. Ten en cuenta que se debe habilitar el acceso mediante correo electrónico/contraseña para utilizar el acceso con un vínculo de correo electrónico.
    3. En la misma sección, habilita el método de acceso con vínculo de correo electrónico (acceso sin contraseña).
    4. Haz clic en Guardar.

Para iniciar el flujo de autenticación, muestra una interfaz que le pida ingresar su dirección de correo electrónico y, luego, llama a sendSignInLinkToEmail() para solicitar a Firebase que envíe el vínculo de autenticación al correo electrónico del usuario.

  1. Construye el objeto ActionCodeSettings, que proporciona a Firebase las instrucciones para construir el vínculo de correo electrónico. Configura los siguientes campos:

    • url: Es el vínculo directo que se debe incorporar y cualquier estado adicional que se deba pasar. El dominio del vínculo debe estar en la lista blanca de dominios autorizados de Firebase console. Para encontrarla, ve a la pestaña Método de acceso (Autenticación -> Método de acceso). El vínculo redireccionará al usuario a esta URL si la app no está instalada en el dispositivo y no se pudo instalar.

    • androidPackageName y IOSBundleId: Son las apps que se deben usar cuando se abre el vínculo de acceso en un dispositivo Android o iOS. Obtén más información sobre la configuración de Firebase Dynamic Links a fin de abrir vínculos con acciones en correos electrónicos a través de apps para dispositivos móviles.

    • handleCodeInApp: Configurado como true. A diferencia de otras acciones de correo electrónico fuera de banda (restablecimiento de contraseñas y verificaciones de correos electrónicos), la operación de acceso siempre debe completarse en la app. Esto se debe a que, al final del flujo, se espera que el usuario acceda y que su estado de autenticación permanezca en la app.

    • dynamicLinkDomain: Si se definen varios dominios de vínculos dinámicos personalizados para un proyecto, especifica cuál usar cuando el vínculo se abra mediante una app para dispositivos móviles determinada (por ejemplo, example.page.link). De lo contrario, el primer dominio se selecciona automáticamente.

    var acs = ActionCodeSettings(
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be whitelisted in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true
        handleCodeInApp: true,
        iOSBundleId: 'com.example.ios',
        androidPackageName: 'com.example.android',
        // installIfNotAvailable
        androidInstallApp: true,
        // minimumVersion
        androidMinimumVersion: '12');
    
  2. Pídele el correo electrónico al usuario.

  3. Envía el vínculo de autenticación al correo electrónico del usuario y guarda su correo en caso de que este complete el acceso con correo electrónico en el mismo dispositivo.

    var emailAuth = 'someemail@domain.com';
    FirebaseAuth.instance.sendSignInLinkToEmail(
            email: emailAuth, actionCodeSettings: acs)
        .catchError((onError) => print('Error sending email verification $onError'))
        .then((value) => print('Successfully sent email verification'));
    });
    

Medidas de seguridad

A fin de evitar que un vínculo de acceso se use para acceder 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 cuando se complete el proceso de acceso. Para que el acceso sea exitoso, esta dirección de correo electrónico debe coincidir con la dirección a la que se envió originalmente el vínculo de acceso.

Con el objetivo de simplificar este proceso para los usuarios que abren el vínculo de acceso en el mismo dispositivo en el que lo solicitan, puedes almacenar su dirección de correo electrónico de manera local (por ejemplo, utilizando SharedPreferences) cuando envías el correo electrónico de acceso. Luego, usa esta dirección para completar el flujo. No pases el correo electrónico del usuario en los parámetros de la URL de redireccionamiento ni vuelvas a utilizarlo, ya que esto puede provocar que se inserte la sesión.

Una vez finalizado el acceso, se eliminará del usuario cualquier mecanismo de acceso previo sin verificar y se invalidarán las sesiones existentes. Por ejemplo, si alguien creó una cuenta no verificada con el mismo correo electrónico y contraseña, se quitará la contraseña del usuario para evitar que el ladrón de identidad que reclamó la propiedad y creó esa cuenta no verificada vuelva a acceder con el correo electrónico y la contraseña no verificados.

Además, asegúrate de utilizar una URL HTTPS en producción para evitar que los servidores intermediarios intercepten el vínculo.

Firebase Authentication utiliza Firebase Dynamic Links para enviar el vínculo de correo electrónico a un dispositivo móvil. Para completar el acceso a través de la aplicación para dispositivos móviles, la app debe configurarse para detectar el vínculo entrante, analizar el vínculo directo subyacente y, luego, completar el acceso.

  1. Configura tu app para recibir Dynamic Links en Flutter en la guía.

  2. En tu controlador de vínculos, verifica si el vínculo está destinado a la autenticación de vínculos de correo electrónico y, de ser así, completa el proceso de acceso.

    // Confirm the link is a sign-in with email link.
    if (FirebaseAuth.instance.isSignInWithEmailLink(emailLink)) {
      try {
        // The client SDK will parse the code from the link for you.
        final userCredential = await FirebaseAuth.instance
            .signInWithEmailLink(email: emailAuth, emailLink: emailLink);
    
        // You can access the new user via userCredential.user.
        final emailAddress = userCredential.user?.email;
    
        print('Successfully signed in with email link!');
      } catch (error) {
        print('Error signing in with email link.');
      }
    }
    

También puedes vincular este método de autenticación a un usuario existente. Por ejemplo, un usuario ya autenticado con otro proveedor, como un número de teléfono, puede agregar este método de acceso a la cuenta existente.

Lo que cambia sería la segunda mitad de la operación:

final authCredential = EmailAuthProvider
    .credentialWithLink(email: emailAuth, emailLink: emailLink.toString());
try {
    await FirebaseAuth.instance.currentUser
        ?.linkWithCredential(authCredential);
} catch (error) {
    print("Error linking emailLink credential.");
}

Esto también se puede usar para volver a autenticar a un usuario de un vínculo de correo electrónico antes de ejecutar una operación sensible.

final authCredential = EmailAuthProvider
    .credentialWithLink(email: emailAuth, emailLink: emailLink.toString());
try {
    await FirebaseAuth.instance.currentUser
        ?.reauthenticateWithCredential(authCredential);
} catch (error) {
    print("Error reauthenticating credential.");
}

Sin embargo, como el flujo podría terminar en un dispositivo diferente, desde donde el usuario original no accedió, dicho flujo podría no completarse. En ese caso, se puede mostrar un error al usuario para forzarlo a abrir el vínculo en el mismo dispositivo. Se puede pasar algún estado en el vínculo para proporcionar información sobre el tipo de operación y el uid del usuario.

En caso de que admitas tanto contraseñas como accesos basados en vínculos de correo electrónico, usa fetchSignInMethodsForEmail para diferenciar el método de acceso para un usuario con contraseña/vínculo. Esto es útil para flujos con identificador inicial, en los que primero se solicita al usuario que proporcione su correo electrónico y, luego, se le presenta el método de acceso:

try {
    final signInMethods =
        await FirebaseAuth.instance.fetchSignInMethodsForEmail(emailAuth);
    final userExists = signInMethods.isNotEmpty;
    final canSignInWithLink = signInMethods
        .contains(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD);
    final canSignInWithPassword = signInMethods
        .contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD);
} on FirebaseAuthException catch (exception) {
    switch (exception.code) {
        case "invalid-email":
            print("Not a valid email address.");
            break;
        default:
            print("Unknown error.");
    }
}

Como se describió anteriormente, correo electrónico/contraseña y correo electrónico/vínculo se consideran como el mismo EmailAuthProvider (el mismo PROVIDER_ID), pero con métodos de acceso diferentes.

Próximos pasos

Cuando un usuario crea una cuenta nueva, esta 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 el método de acceso que usó.

En tus apps, puedes obtener la información básica de perfil del usuario a partir del objeto User. Consulta Administrar usuarios.

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 la app con 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():

await FirebaseAuth.instance.signOut();