您可以使用 Firebase 身份验证通过向用户发送一封包含链接的电子邮件来登录用户,他们可以单击该链接进行登录。在此过程中,用户的电子邮件地址也会得到验证。
通过电子邮件登录有很多好处:
- 低摩擦注册和登录。
- 降低跨应用程序重复使用密码的风险,即使是精心挑选的密码也会破坏安全性。
- 能够对用户进行身份验证,同时还验证用户是电子邮件地址的合法所有者。
- 用户只需要一个可访问的电子邮件帐户即可登录。不需要电话号码或社交媒体帐户的所有权。
- 用户无需提供(或记住)密码即可安全登录,这在移动设备上可能很麻烦。
- 之前使用电子邮件标识符(密码或联合)登录的现有用户可以升级为仅使用电子邮件登录。例如,忘记密码的用户仍然可以登录而无需重设密码。
在你开始之前
设置您的 Android 项目
如果您还没有,请将 Firebase 添加到您的 Android 项目中。
在您的模块(应用级)Gradle 文件(通常为
<project>/<app-module>/build.gradle
)中,添加 Firebase 身份验证 Android 库的依赖项。我们建议使用Firebase Android BoM来控制库版本。此外,作为设置 Firebase 身份验证的一部分,您需要将 Google Play 服务 SDK 添加到您的应用程序中。
Kotlin+KTX
dependencies { // Import the BoM for the Firebase platform implementation platform('com.google.firebase:firebase-bom:31.2.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.1' }通过使用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.1' }Java
dependencies { // Import the BoM for the Firebase platform implementation platform('com.google.firebase:firebase-bom:31.2.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.1' }通过使用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.1' }
为您的 Firebase 项目启用电子邮件链接登录
要通过电子邮件链接登录用户,您必须首先为您的 Firebase 项目启用电子邮件提供商和电子邮件链接登录方法:
- 在Firebase 控制台中,打开Auth部分。
- 在登录方法选项卡上,启用电子邮件/密码提供程序。请注意,必须启用电子邮件/密码登录才能使用电子邮件链接登录。
- 在同一部分中,启用电子邮件链接(无密码登录)登录方法。
- 单击保存。
将身份验证链接发送到用户的电子邮件地址
要启动身份验证流程,请向用户显示一个界面,提示用户提供其电子邮件地址,然后调用sendSignInLinkToEmail
以请求 Firebase 将身份验证链接发送到用户的电子邮件。
构建ActionCodeSettings对象,它为 Firebase 提供有关如何构建电子邮件链接的说明。设置以下字段:
-
url
:要嵌入的深层链接和要传递的任何其他状态。链接的域必须在授权域的 Firebase 控制台列表中列入白名单,可以通过转到登录方法选项卡(身份验证 -> 登录方法)找到该列表。如果该应用程序未安装在他们的设备上并且无法安装该应用程序,则该链接会将用户重定向到此 URL。 -
androidPackageName
和IOSBundleId
:在 Android 或 Apple 设备上打开登录链接时要使用的应用程序。详细了解如何配置 Firebase 动态链接以通过移动应用程序打开电子邮件操作链接。 -
handleCodeInApp
:设置为 true。与其他带外电子邮件操作(密码重置和电子邮件验证)不同,登录操作必须始终在应用程序中完成。这是因为,在流程结束时,用户应该登录并且他们的身份验证状态会保留在应用程序中。 -
dynamicLinkDomain
:当为一个项目定义了多个自定义动态链接域时,指定在通过指定的移动应用程序(例如example.page.link
)打开链接时使用哪个域。否则会自动选择第一个域。
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 */) }
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();
要了解有关 ActionCodeSettings 的更多信息,请参阅电子邮件操作中的传递状态部分。
-
向用户询问他们的电子邮件。
将身份验证链接发送到用户的电子邮件,并保存用户的电子邮件,以防用户在同一设备上完成电子邮件登录。
Kotlin+KTX
Firebase.auth.sendSignInLinkToEmail(email, actionCodeSettings) .addOnCompleteListener { task -> if (task.isSuccessful) { Log.d(TAG, "Email sent.") } }
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."); } } });
使用电子邮件链接完成登录
安全问题
为防止登录链接被用于以非预期用户身份或在非预期设备上登录,Firebase Auth 要求在完成登录流程时提供用户的电子邮件地址。要成功登录,此电子邮件地址必须与登录链接最初发送到的地址匹配。
对于在请求链接的同一设备上打开登录链接的用户,您可以在发送登录电子邮件时通过在本地存储他们的电子邮件地址(例如使用 SharedPreferences)来简化此流程。然后,使用这个地址来完成流程。不要在重定向 URL 参数中传递用户的电子邮件并重新使用它,因为这可能会启用会话注入。
登录完成后,任何先前未经验证的登录机制都将从用户中删除,并且任何现有会话都将失效。例如,如果某人之前使用相同的电子邮件和密码创建了一个未经验证的帐户,则用户的密码将被删除,以防止声称拥有所有权并创建该未经验证的帐户的冒名顶替者使用未经验证的电子邮件和密码再次登录。
还要确保您在生产中使用 HTTPS URL,以避免您的链接可能被中间服务器拦截。
在 Android 应用程序中完成登录
Firebase 身份验证使用 Firebase 动态链接将电子邮件链接发送到移动设备。对于通过移动应用程序完成登录,应用程序必须配置为检测传入的应用程序链接,解析底层深层链接,然后完成登录。
配置 Firebase 动态链接
Firebase Auth 在发送要在移动应用程序中打开的链接时使用Firebase 动态链接。要使用此功能,必须在 Firebase 控制台中配置动态链接。
启用 Firebase 动态链接:
- 在Firebase 控制台中,打开动态链接部分。
如果您尚未接受动态链接条款并创建动态链接域,请立即接受。
如果您已经创建了动态链接域,请记下它。动态链接域通常类似于以下示例:
example.page.link
当您配置 Apple 或 Android 应用程序以拦截传入链接时,您将需要此值。
配置 Android 应用程序:
- 为了从您的 Android 应用程序处理这些链接,需要在 Firebase 控制台项目设置中指定 Android 包名称。此外,还需要提供申请证书的SHA-1和SHA-256。
- 现在您已经添加了一个动态链接域并确保您的 Android 应用程序配置正确,动态链接将从启动器活动开始重定向到您的应用程序。
- 如果您希望动态链接重定向到特定活动,您将需要在AndroidManifest.xml文件中配置一个 Intent 过滤器。这可以通过在意图过滤器中指定动态链接域或电子邮件操作处理程序来完成。默认情况下,电子邮件操作处理程序托管在如下示例的域中:
PROJECT_ID.firebaseapp.com/
- 注意事项:
- 不要指定您在 intent 过滤器的 actionCodeSettings 上设置的 URL。
- 在创建动态链接域时,您可能还创建了一个短 URL 链接。这个短网址不会被传递;不要配置您的意图过滤器以使用
android:pathPrefix
属性捕获它。这意味着您将无法在应用程序的不同部分捕获不同的动态链接。但是,您可以检查链接中的mode
查询参数以查看正在尝试执行的操作,或使用isSignInWithEmailLink
等 SDK 方法查看您的应用收到的链接是否符合您的要求。
- 更多关于接收动态链接的内容,请参考接收Android动态链接说明。
验证链接并登录
收到上述链接后,验证它是否用于电子邮件链接身份验证并完成登录。
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) } } }
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()); } } }); }
要了解有关如何在 Apple 应用程序中使用电子邮件链接处理登录的更多信息,请参阅Apple 平台指南。
要了解如何在 Web 应用程序中处理使用电子邮件链接登录,请参阅Web 指南。
使用电子邮件链接链接/重新验证
您还可以将此身份验证方法链接到现有用户。例如,以前通过另一个提供商(例如电话号码)进行身份验证的用户可以将这种登录方法添加到他们现有的帐户中。
不同之处在于操作的后半部分:
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); // 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) // 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) } }
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()); } } });
但是,由于流程可能会在原始用户未登录的其他设备上结束,因此此流程可能无法完成。在这种情况下,可以向用户显示错误以强制他们在同一设备上打开链接。某些状态可以在链接中传递,以提供有关操作类型和用户 uid 的信息。
区分电子邮件/密码和电子邮件链接
如果您同时支持使用电子邮件登录密码和基于链接的登录,要区分密码/链接用户的登录方法,请使用fetchSignInMethodsForEmail
。这对于首先要求用户提供他们的电子邮件然后提供登录方法的标识符优先流程很有用:
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) }
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()); } } });
如上所述,电子邮件/密码和电子邮件/链接被视为具有不同登录方法的相同EmailAuthProvider
(相同的PROVIDER_ID
)。
下一步
用户首次登录后,将创建一个新的用户帐户并将其链接到用户登录所用的凭据,即用户名和密码、电话号码或身份验证提供商信息。这个新帐户存储为您的 Firebase 项目的一部分,可用于在项目中的每个应用程序中识别用户,无论用户如何登录。
在您的应用中,您可以从
FirebaseUser
对象获取用户的基本个人资料信息。请参阅管理用户。在您的 Firebase Realtime Database 和 Cloud Storage Security Rules中,您可以从
auth
变量中获取登录用户的唯一用户 ID,并使用它来控制用户可以访问的数据。
您可以允许用户使用多个身份验证提供程序登录您的应用程序,方法是将身份验证提供程序凭据链接到现有用户帐户。
要注销用户,请调用signOut
:
Kotlin+KTX
Firebase.auth.signOut()
Java
FirebaseAuth.getInstance().signOut();