Autentica con Firebase mediante un vínculo de correo electrónico en Android

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:

  • Hay pocas restricciones para 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 medios sociales.
  • Un 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

Configura el proyecto de Android Studio

  1. Agrega Firebase al proyecto de Android.
  2. Agrega las dependencias para Firebase Authentication y Servicios de Google Play a tu archivo build.gradle de nivel de app:
    implementation 'com.google.firebase:firebase-auth:16.0.4'
    implementation 'com.google.android.gms:play-services-auth:16.0.1'
    

A fin de 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 de correo electrónico y con 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 mediante un vínculo de correo electrónico.
  3. En la misma sección, habilita el método de acceso mediante Vínculo del correo electrónico (acceso sin contraseña).
  4. Haz clic en Guardar.

Para iniciar el flujo de autenticación, muéstrale al usuario 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, el cual proporciona a Firebase las instrucciones sobre cómo construir el vínculo de correo electrónico. Configura los siguientes campos:

    • url: El vínculo directo que se debe incorporar y cualquier estado adicional que haya que divulgar. 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 (Authentication -> 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: 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 cómo configurar Firebase Dynamic Links para ejecutar acciones en correos electrónicos a través de apps para dispositivos móviles.
    • handleCodeInApp: Asígnale el valor 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 Auth permanezca en la app.

    Java
    Android

    ActionCodeSettings actionCodeSettings =
            ActionCodeSettings.newBuilder()
                    // URL you want to redirect back to. The domain (www.example.com) for this
                    // URL must be whitelisted in the Firebase Console.
                    .setUrl("https://www.example.com/finishSignUp?cartId=1234")
                    // This must be true
                    .setHandleCodeInApp(true)
                    .setIOSBundleId("com.example.ios")
                    .setAndroidPackageName(
                            "com.example.android",
                            true, /* installIfNotAvailable */
                            "12"    /* minimumVersion */)
                    .build();

    Kotlin
    Android

    val actionCodeSettings = ActionCodeSettings.newBuilder()
            // URL you want to redirect back to. The domain (www.example.com) for this
            // URL must be whitelisted in the Firebase Console.
            .setUrl("https://www.example.com/finishSignUp?cartId=1234")
            // This must be true
            .setHandleCodeInApp(true)
            .setIOSBundleId("com.example.ios")
            .setAndroidPackageName(
                    "com.example.android",
                    true, /* installIfNotAvailable */
                    "12"    /* minimumVersion */)
            .build()

    Para obtener más información sobre ActionCodeSettings, consulta la sección Estado de paso en acciones de correo electrónico.

  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.

    Java
    Android

    FirebaseAuth auth = FirebaseAuth.getInstance();
    auth.sendSignInLinkToEmail(email, actionCodeSettings)
            .addOnCompleteListener(new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Email sent.");
                    }
                }
            });

    Kotlin
    Android

    val auth = FirebaseAuth.getInstance()
    auth.sendSignInLinkToEmail(email, actionCodeSettings)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    Log.d(TAG, "Email sent.")
                }
            }

Preocupaciones 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 flujo 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 flujo 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 redireccionamiento de URL ni vuelvas a utilizarlo, ya que esto puede provocar inyecciones de 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ó previamente una cuenta no verificada con el mismo correo electrónico y contraseña, se eliminará 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 tu vínculo sea interceptado por los servidores intermediarios.

Cómo completar el acceso en una app para Android

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.

Firebase Auth utiliza Firebase Dynamic Links cuando envía un vínculo que debe abrirse en una aplicación para dispositivos móviles. Para poder usar esta función, se debe configurar Dynamic Links en Firebase console.

  1. Habilita Firebase Dynamic Links:

    1. En Firebase console, abre la sección Dynamic Links.
    2. Si aún no has aceptado los términos ni has creado un dominio de Dynamic Links, deberás hacerlo ahora.

      Si ya creaste un dominio de Dynamic Links, anótalo. Los dominios suelen ser como el ejemplo siguiente:

      example.page.link

      Necesitarás este valor cuando configures la app para iOS o Android a fin de interceptar el vínculo entrante.

  2. Configuración de aplicaciones para Android:

    1. Para manejar estos vínculos desde la aplicación para Android, es necesario especificar el nombre del paquete de Android en la configuración del proyecto de Firebase console. Además, es necesario proporcionar el SHA-1 y el SHA-256 del certificado de aplicación.
    2. Ahora que agregaste un dominio de Dynamic Links y te aseguraste de que tu aplicación de Android esté configurada correctamente, el vínculo dinámico te redireccionará a tu aplicación, a partir de la actividad iniciadora.
    3. Si deseas que el vínculo dinámico redireccione a una actividad específica, debes configurar un filtro de intent en el archivo AndroidManifest.xml. Para ello, especifica el dominio de Dynamic Links o el controlador de acciones de correo electrónico en el filtro de intent. De manera predeterminada, el controlador de acciones de correo electrónico se aloja en un dominio como se muestra en el siguiente ejemplo:
      PROJECT_ID.firebaseapp.com/
    4. Advertencias:
      1. No especifiques la URL que estableciste en actionCodeSettings en tu filtro de intent.
      2. Al crear el dominio de Dynamic Links, es posible que hayas creado también un vínculo de URL corta. Esta URL corta no se pasará; no configures el filtro de intent filter para capturarlo con un atributo android:pathPrefix. Esto significa que no podrás capturar diferentes vínculos dinámicos en diferentes partes de tu aplicación. Sin embargo, puedes revisar el parámetro de consulta mode en el vínculo para comprobar qué operación se intenta realizar o usar los métodos de SDK como isSignInWithEmailLink para ver si el vínculo que la aplicación recibe hace lo que deseas.
    5. Para obtener más información sobre la recepción de Dynamic Links, consulta las instrucciones para recibir Dynamic Links en Android.

Una vez que recibas el vínculo mencionado anteriormente, verifica que sea apto para la autenticación mediante vínculo de correo electrónico y completa el acceso.

Java
Android

FirebaseAuth auth = FirebaseAuth.getInstance();
Intent intent = getIntent();
String emailLink = intent.getData().toString();

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    String email = "someemail@domain.com";

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
            .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Successfully signed in with email link!");
                        AuthResult result = task.getResult();
                        // You can access the new user via result.getUser()
                        // Additional user info profile *not* available via:
                        // result.getAdditionalUserInfo().getProfile() == null
                        // You can check if the user is new or existing:
                        // result.getAdditionalUserInfo().isNewUser()
                    } else {
                        Log.e(TAG, "Error signing in with email link", task.getException());
                    }
                }
            });
}

Kotlin
Android

val auth = FirebaseAuth.getInstance()
val intent = intent
val emailLink = intent.data!!.toString()

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    val email = "someemail@domain.com"

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    Log.d(TAG, "Successfully signed in with email link!")
                    val result = task.result
                    // You can access the new user via result.getUser()
                    // Additional user info profile *not* available via:
                    // result.getAdditionalUserInfo().getProfile() == null
                    // You can check if the user is new or existing:
                    // result.getAdditionalUserInfo().isNewUser()
                } else {
                    Log.e(TAG, "Error signing in with email link", task.exception)
                }
            }
}

Para obtener más información sobre cómo realizar el acceso mediante un vínculo de correo electrónico en una aplicación para iOS, consulta la Guía para iOS.

Para saber cómo realizar el acceso mediante un vínculo de correo electrónico en una aplicación web, consulta la Guía para páginas web.

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:

Java
Android

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Link the credential to the current user.
auth.getCurrentUser().linkWithCredential(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Log.d(TAG, "Successfully linked emailLink credential!");
                    AuthResult result = task.getResult();
                    // You can access the new user via result.getUser()
                    // Additional user info profile *not* available via:
                    // result.getAdditionalUserInfo().getProfile() == null
                    // You can check if the user is new or existing:
                    // result.getAdditionalUserInfo().isNewUser()
                } else {
                    Log.e(TAG, "Error linking emailLink credential", task.getException());
                }
            }
        });

Kotlin
Android

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Link the credential to the current user.
auth.currentUser!!.linkWithCredential(credential)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                Log.d(TAG, "Successfully linked emailLink credential!")
                val result = task.result
                // You can access the new user via result.getUser()
                // Additional user info profile *not* available via:
                // result.getAdditionalUserInfo().getProfile() == null
                // You can check if the user is new or existing:
                // result.getAdditionalUserInfo().isNewUser()
            } else {
                Log.e(TAG, "Error linking emailLink credential", task.exception)
            }
        }

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 confidencial.

Java
Android

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Re-authenticate the user with this credential.
auth.getCurrentUser().reauthenticateAndRetrieveData(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    // User is now successfully reauthenticated
                } else {
                    Log.e(TAG, "Error reauthenticating", task.getException());
                }
            }
        });

Kotlin
Android

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Re-authenticate the user with this credential.
auth.currentUser!!.reauthenticateAndRetrieveData(credential)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // User is now successfully reauthenticated
            } else {
                Log.e(TAG, "Error reauthenticating", task.exception)
            }
        }

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 de un usuario con contraseña/vínculo. Esto es útil para flujos con identificador primero, en los que primero se solicita al usuario que proporcione su correo electrónico y luego se le presenta el método de acceso:

Java
Android

auth.fetchSignInMethodsForEmail(email)
        .addOnCompleteListener(new OnCompleteListener<SignInMethodQueryResult>() {
            @Override
            public void onComplete(@NonNull Task<SignInMethodQueryResult> task) {
                if (task.isSuccessful()) {
                    SignInMethodQueryResult result = task.getResult();
                    List<String> signInMethods = result.getSignInMethods();
                    if (signInMethods.contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD)) {
                        // User can sign in with email/password
                    } else if (signInMethods.contains(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD)) {
                        // User can sign in with email/link
                    }
                } else {
                    Log.e(TAG, "Error getting sign in methods for user", task.getException());
                }
            }
        });

Kotlin
Android

auth.fetchSignInMethodsForEmail(email)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                val result = task.result
                val signInMethods = result.signInMethods
                if (signInMethods!!.contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD)) {
                    // User can sign in with email/password
                } else if (signInMethods.contains(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD)) {
                    // User can sign in with email/link
                }
            } else {
                Log.e(TAG, "Error getting sign in methods for user", task.exception)
            }
        }

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 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) con las que accedió el usuario. Esta cuenta nueva se almacena como parte del proyecto de Firebase y puede usarse para identificar a un usuario en todas las apps del proyecto, sin importar el método de acceso que se use.

  • En las apps, puedes obtener la información básica de perfil del usuario a partir del objeto FirebaseUser. Consulta Administra usuarios.

  • En las reglas de seguridad de Firebase Realtime Database y Cloud Storage, puedes obtener el ID único del usuario que accedió a partir de la variable auth y usarlo para controlar los datos a los que tiene acceso.

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

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

Java
Android

FirebaseAuth.getInstance().signOut();

Kotlin
Android

FirebaseAuth.getInstance().signOut()

Enviar comentarios sobre…

¿Necesitas ayuda? Visita nuestra página de asistencia.