获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

在 Android 中使用电子邮件链接通过 Firebase 进行身份验证

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

您可以使用 Firebase 身份验证通过向用户发送包含链接的电子邮件来登录用户,他们可以单击该链接进行登录。在此过程中,还会验证用户的电子邮件地址。

通过电子邮件登录有很多好处:

  • 低摩擦注册和登录。
  • 降低跨应用程序重复使用密码的风险,这可能会破坏即使是精心挑选的密码的安全性。
  • 能够验证用户身份,同时验证用户是否是电子邮件地址的合法所有者。
  • 用户只需要一个可访问的电子邮件帐户即可登录。不需要拥有电话号码或社交媒体帐户。
  • 用户无需提供(或记住)密码即可安全登录,这在移动设备上可能很麻烦。
  • 以前使用电子邮件标识符(密码或联合身份)登录的现有用户可以升级为仅使用电子邮件登录。例如,忘记密码的用户仍然可以登录而无需重置密码。

在你开始之前

设置您的 Android 项目

  1. 如果您还没有,请将 Firebase 添加到您的 Android 项目中。

  2. 在您的模块(应用级)Gradle 文件(通常是<project>/<app-module>/build.gradle )中,添加 Firebase Authentication Android 库的依赖项。我们建议使用Firebase Android BoM来控制库版本控制。

    此外,作为设置 Firebase 身份验证的一部分,您需要将 Google Play 服务 SDK 添加到您的应用中。

    Java

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:31.1.0')
    
        // Add the dependency for the Firebase Authentication library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-auth'
    // Also add the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.4.0'
    }

    通过使用Firebase Android BoM ,您的应用将始终使用兼容版本的 Firebase Android 库。

    (替代)使用 BoM 的情况下添加 Firebase 库依赖项

    如果您选择不使用 Firebase BoM,则必须在其依赖行中指定每个 Firebase 库版本。

    请注意,如果您在应用中使用多个Firebase 库,我们强烈建议您使用 BoM 来管理库版本,以确保所有版本都兼容。

    dependencies {
        // Add the dependency for the Firebase Authentication library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-auth:21.1.0'
    // Also add the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.4.0'
    }

    Kotlin+KTX

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:31.1.0')
    
        // Add the dependency for the Firebase Authentication library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-auth-ktx'
    // Also add the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.4.0'
    }

    通过使用Firebase Android BoM ,您的应用将始终使用兼容版本的 Firebase Android 库。

    (替代)使用 BoM 的情况下添加 Firebase 库依赖项

    如果您选择不使用 Firebase BoM,则必须在其依赖行中指定每个 Firebase 库版本。

    请注意,如果您在应用中使用多个Firebase 库,我们强烈建议您使用 BoM 来管理库版本,以确保所有版本都兼容。

    dependencies {
        // Add the dependency for the Firebase Authentication library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-auth-ktx:21.1.0'
    // Also add the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.4.0'
    }

要通过电子邮件链接登录用户,您必须首先为您的 Firebase 项目启用电子邮件提供商和电子邮件链接登录方法:

  1. Firebase 控制台中,打开Auth部分。
  2. 登录方法选项卡上,启用电子邮件/密码提供程序。请注意,必须启用电子邮件/密码登录才能使用电子邮件链接登录。
  3. 在同一部分中,启用电子邮件链接(无密码登录)登录方法。
  4. 单击保存

要启动身份验证流程,请向用户显示一个界面,提示用户提供他们的电子邮件地址,然后调用sendSignInLinkToEmail以请求 Firebase 将身份验证链接发送到用户的电子邮件。

  1. 构造ActionCodeSettings对象,该对象为 Firebase 提供有关如何构造电子邮件链接的说明。设置以下字段:

    • url :要嵌入的深层链接和要传递的任何其他状态。链接的域必须在授权域的 Firebase 控制台列表中列入白名单,可以通过转到登录方法选项卡(身份验证 -> 登录方法)找到该列表。如果应用程序未安装在用户的设备上并且无法安装该应用程序,该链接会将用户重定向到此 URL。
    • androidPackageNameIOSBundleId :在 Android 或 Apple 设备上打开登录链接时使用的应用程序。详细了解如何配置 Firebase 动态链接以通过移动应用打开电子邮件操作链接。
    • handleCodeInApp :设置为真。与其他带外电子邮件操作(密码重置和电子邮件验证)不同,登录操作必须始终在应用程序中完成。这是因为,在流程结束时,预计用户会登录,并且他们的身份验证状态会保留在应用程序中。
    • dynamicLinkDomain :当为一个项目定义了多个自定义动态链接域时,指定在通过指定的移动应用程序(例如example.page.link )打开链接时使用哪一个。否则将自动选择第一个域。

    Java

    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+KTX

    val actionCodeSettings = 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
        setIOSBundleId("com.example.ios")
        setAndroidPackageName(
                "com.example.android",
                true, /* installIfNotAvailable */
                "12" /* minimumVersion */)
    }

    要了解有关 ActionCodeSettings 的更多信息,请参阅电子邮件操作中的传递状态部分。

  2. 向用户询问他们的电子邮件。

  3. 将认证链接发送到用户邮箱,并保存用户邮箱,以防用户在同一设备上完成邮箱登录。

    Java

    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+KTX

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

安全问题

为防止使用登录链接以非预期用户身份或在非预期设备上登录,Firebase 身份验证要求在完成登录流程时提供用户的电子邮件地址。要成功登录,此电子邮件地址必须与最初发送登录链接的地址相匹配。

当您发送登录电子邮件时,您可以通过在本地存储他们的电子邮件地址(例如使用 SharedPreferences)来为在请求链接的同一设备上打开登录链接的用户简化此流程。然后,使用此地址完成流程。不要在重定向 URL 参数中传递用户的电子邮件并重新使用它,因为这可能会启用会话注入。

登录完成后,任何以前未经验证的登录机制都将从用户中删除,任何现有会话都将失效。例如,如果有人之前使用相同的电子邮件和密码创建了一个未经验证的帐户,则该用户的密码将被删除,以防止声称拥有所有权并创建该未经验证的帐户的冒充者使用未经验证的电子邮件和密码再次登录。

还要确保您在生产中使用 HTTPS URL,以避免您的链接可能被中间服务器拦截。

在 Android 应用中完成登录

Firebase 身份验证使用 Firebase 动态链接将电子邮件链接发送到移动设备。对于通过移动应用程序完成登录,必须将应用程序配置为检测传入的应用程序链接,解析底层深层链接,然后完成登录。

Firebase Auth 在发送要在移动应用程序中打开的链接时使用Firebase 动态链接。要使用此功能,必须在 Firebase 控制台中配置动态链接。

  1. 启用 Firebase 动态链接:

    1. Firebase 控制台中,打开动态链接部分。
    2. 如果您尚未接受动态链接条款并创建动态链接域,请立即执行。

      如果您已经创建了动态链接域,请记下它。动态链接域通常类似于以下示例:

      example.page.link

      当您配置 Apple 或 Android 应用程序以拦截传入链接时,您将需要此值。

  2. 配置 Android 应用程序:

    1. 为了处理来自您的 Android 应用程序的这些链接,需要在 Firebase 控制台项目设置中指定 Android 包名称。另外,需要提供申请证书的SHA-1和SHA-256。
    2. 现在您已经添加了一个动态链接域并确保您的 Android 应用程序配置正确,动态链接将从启动器活动开始重定向到您的应用程序。
    3. 如果您希望动态链接重定向到特定活动,则需要在AndroidManifest.xml文件中配置意图过滤器。这可以通过在意图过滤器中指定您的动态链接域或电子邮件操作处理程序来完成。默认情况下,电子邮件操作处理程序托管在类似于以下示例的域中:
      PROJECT_ID.firebaseapp.com/
    4. 注意事项:
      1. 不要指定您在意图过滤器中的 actionCodeSettings 上设置的 URL。
      2. 创建动态链接域时,您可能还创建了一个短 URL 链接。这个短网址不会被传递;不要将您的意图过滤器配置为使用android:pathPrefix属性来捕获它。这意味着您将无法在应用程序的不同部分捕获不同的动态链接。但是,您可以检查链接中的mode查询参数以查看正在尝试执行的操作,或者使用 SDK 方法(例如isSignInWithEmailLink )查看您的应用收到的链接是否符合您的要求。
    5. 有关接收动态链接的更多信息,请参阅接收 Android 动态链接说明

收到如上所述的链接后,验证它是否用于电子邮件链接身份验证并完成登录。

Java

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+KTX

val auth = Firebase.auth
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)
                }
            }
}

要了解有关如何在 Apple 应用程序中使用电子邮件链接登录的更多信息,请参阅Apple 平台指南

要了解如何在 Web 应用程序中使用电子邮件链接处理登录,请参阅Web 指南

您还可以将此身份验证方法链接到现有用户。例如,以前通过另一个提供商(例如电话号码)进行身份验证的用户可以将此登录方法添加到他们现有的帐户中。

不同之处在于操作的后半部分:

Java

// 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+KTX

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

// Link the credential to the current user.
Firebase.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)
            }
        }

这也可用于在运行敏感操作之前重新验证电子邮件链接用户。

Java

// 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+KTX

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

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

但是,由于流程可能最终在原始用户未登录的不同设备上结束,因此此流程可能无法完成。在这种情况下,可以向用户显示错误以强制他们在同一设备上打开链接。可以在链接中传递一些状态,以提供有关操作类型和用户 uid 的信息。

如果您同时支持密码和基于链接的电子邮件登录,为了区分密码/链接用户的登录方法,请使用fetchSignInMethodsForEmail 。这对于首先要求用户提供其电子邮件然后向其显示登录方法的标识符优先流程很有用:

Java

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+KTX

Firebase.auth.fetchSignInMethodsForEmail(email)
        .addOnSuccessListener { 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
            }
        }
        .addOnFailureListener { exception ->
            Log.e(TAG, "Error getting sign in methods for user", exception)
        }

如上所述,电子邮件/密码和电子邮件/链接被视为具有不同登录方法的相同EmailAuthProvider (相同的PROVIDER_ID )。

下一步

用户首次登录后,会创建一个新用户帐户并将其链接到凭据(即用户名和密码、电话号码或身份验证提供商信息),即用户登录时使用的凭据。这个新帐户作为 Firebase 项目的一部分存储,可用于在项目中的每个应用中识别用户,无论用户如何登录。

  • 在您的应用中,您可以从FirebaseUser对象获取用户的基本个人资料信息。请参阅管理用户

  • 在您的 Firebase 实时数据库和云存储安全规则中,您可以从auth变量中获取登录用户的唯一用户 ID,并使用它来控制用户可以访问哪些数据。

您可以通过将身份验证提供程序凭据链接到现有用户帐户来允许用户使用多个身份验证提供程序登录您的应用程序。

要注销用户,请调用signOut

Java

FirebaseAuth.getInstance().signOut();

Kotlin+KTX

Firebase.auth.signOut()