在 Android 中使用電子郵件鏈接通過 Firebase 進行身份驗證

您可以使用 Firebase 身份驗證通過向用戶發送包含鏈接的電子郵件來登錄用戶,用戶可以點擊該鏈接登錄。在此過程中,還會驗證用戶的電子郵件地址。

通過電子郵件登錄有很多好處:

  • 低摩擦註冊和登錄。
  • 降低跨應用程序重複使用密碼的風險,這可能會破壞即使是精心選擇的密碼的安全性。
  • 在驗證用戶身份的同時驗證用戶是否是電子郵件地址的合法所有者的能力。
  • 用戶只需要一個可訪問的電子郵件帳戶即可登錄。不需要電話號碼或社交媒體帳戶的所有權。
  • 用戶無需提供(或記住)密碼即可安全登錄,這在移動設備上可能很麻煩。
  • 以前使用電子郵件標識符(密碼或聯合身份驗證)登錄的現有用戶可以升級為僅使用電子郵件登錄。例如,忘記密碼的用戶仍然可以登錄而無需重置密碼。

在你開始之前

設置您的 Android 項目

  1. 如果你還沒有,添加火力地堡到您的Android項目

  2. 使用火力地堡Android的物料清單,聲明你的模塊(應用程序級)搖籃文件(通常為火力地堡認證的Android庫的依賴app/build.gradle )。

    此外,作為設置 Firebase 身份驗證的一部分,您需要將 Google Play 服務 SDK 添加到您的應用程序。

    爪哇

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:29.0.3')
    
        // Declare 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 declare the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.0.1'
    }

    通過使用火力地堡Android的物料清單,您的應用程序將始終使用火力地堡的Android庫的兼容版本。

    (替代)聲明火力地堡庫依賴使用物料清單

    如果您選擇不使用 Firebase BoM,則必須在其依賴項行中指定每個 Firebase 庫版本。

    需要注意的是,如果你在你的應用程序使用多個火力地堡庫,我們強烈建議您使用的物料清單管理庫版本,以保證所有版本相互兼容。

    dependencies {
        // Declare 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.0.1'
    // Also declare the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.0.1'
    }

    科特林+KTX

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:29.0.3')
    
        // Declare 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 declare the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.0.1'
    }

    通過使用火力地堡Android的物料清單,您的應用程序將始終使用火力地堡的Android庫的兼容版本。

    (替代)聲明火力地堡庫依賴使用物料清單

    如果您選擇不使用 Firebase BoM,則必須在其依賴項行中指定每個 Firebase 庫版本。

    需要注意的是,如果你在你的應用程序使用多個火力地堡庫,我們強烈建議您使用的物料清單管理庫版本,以保證所有版本相互兼容。

    dependencies {
        // Declare 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.0.1'
    // Also declare the dependency for the Google Play services library and specify its version implementation 'com.google.android.gms:play-services-auth:20.0.1'
    }

要通過電子郵件鏈接登錄用戶,您必須首先為 Firebase 項目啟用電子郵件提供商和電子郵件鏈接登錄方法:

  1. 火力地堡控制台,打開驗證部分。
  2. 登錄方法選項卡,選中電子郵件/密碼提供商。請注意,必須啟用電子郵件/密碼登錄才能使用電子郵件鏈接登錄。
  3. 在相同的部分,使電子郵件的鏈接(無密碼登錄)登錄方法。
  4. 單擊保存

啟動認證流程,向用戶呈現一個界面,提示用戶提供他們的電子郵件地址,然後調用sendSignInLinkToEmail到請求火力地堡發送的驗證鏈接到用戶的電子郵件。

  1. 構建ActionCodeSettings對象,它提供火力地堡就如何構建電子郵件鏈接指令。設置以下字段:

    • url :深層鏈接嵌入和任何其他國家要傳承下去。該鏈接的域必須在 Firebase 控制台授權域列表中列入白名單,可以通過轉到登錄方法選項卡(身份驗證 -> 登錄方法)找到該列表。如果應用程序未安裝在用戶的設備上並且無法安裝該應用程序,則該鏈接會將用戶重定向到此 URL。
    • androidPackageNameIOSBundleId :這些應用時的登錄鏈接打開Android或蘋果設備上使用。了解更多關於如何配置火力地堡動態鏈接通過移動應用程序打開電子郵件操作鏈接。
    • handleCodeInApp :設置為true。與其他帶外電子郵件操作(密碼重置和電子郵件驗證)不同,登錄操作必須始終在應用程序中完成。這是因為,在流程結束時,用戶需要登錄並且他們的 Auth 狀態會保留在應用程序中。
    • dynamicLinkDomain :當多個自定義動態鏈接結構域的一個項目中,指定當鏈路是通過指定的移動應用被打開使用哪一個(例如,定義example.page.link )。否則將自動選擇第一個域。

    爪哇

    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();

    科特林+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. 將身份驗證鏈接發送到用戶的電子郵件,並保存用戶的電子郵件,以防用戶在同一設備上完成電子郵件登錄。

    爪哇

    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.");
                    }
                }
            });

    科特林+KTX

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

安全問題

為防止使用登錄鏈接以意外用戶身份或在意外設備上登錄,Firebase 身份驗證要求在完成登錄流程時提供用戶的電子郵件地址。要成功登錄,此電子郵件地址必須與登錄鏈接最初發送到的地址相匹配。

您可以為在請求鏈接的同一設備上打開登錄鏈接的用戶簡化此流程,方法是在您發送登錄電子郵件時將他們的電子郵件地址存儲在本地(例如使用 SharedPreferences)。然後,使用此地址完成流程。不要在重定向 URL 參數中傳遞用戶的電子郵件並重新使用它,因為這可能會啟用會話注入。

登錄完成後,任何以前未經驗證的登錄機制將從用戶中刪除,任何現有會話都將失效。例如,如果有人之前使用相同的電子郵件和密碼創建了一個未經驗證的帳戶,則該用戶的密碼將被刪除,以防止聲稱擁有所有權並創建該未經驗證帳戶的冒充者再次使用未經驗證的電子郵件和密碼登錄。

還要確保您在生產中使用 HTTPS URL,以避免您的鏈接可能被中間服務器攔截。

在 Android 應用程序中完成登錄

Firebase 身份驗證使用 Firebase 動態鏈接將電子郵件鏈接發送到移動設備。對於通過移動應用程序完成登錄,應用程序必須配置為檢測傳入的應用程序鏈接,解析底層深層鏈接,然後完成登錄。

火力地堡驗證使用火力地堡動態鏈接發送一個,就是在移動應用程序中打開鏈接時。為了使用此功能,動態鏈接必須在火力地堡控制台來配置。

  1. 啟用 Firebase 動態鏈接:

    1. 火力地堡控制台,打開動態鏈接部分。
    2. 如果您尚未接受動態鏈接條款並創建了動態鏈接域,請立即執行。

      如果您已經創建了動態鏈接域,請記下它。動態鏈接域通常類似於以下示例:

      example.page.link

      當您配置 Apple 或 Android 應用程序以攔截傳入鏈接時,您將需要此值。

  2. 配置安卓應用程序:

    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的動態鏈接說明

收到上述鏈接後,請驗證它是否用於電子郵件鏈接身份驗證並完成登錄。

爪哇

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());
                    }
                }
            });
}

科特林+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)
                }
            }
}

要了解更多關於如何處理登錄,在蘋果應用電子郵件鏈接,請參閱蘋果平台的指南

要了解如何處理登錄在Web應用程序的電子郵件鏈接,請參閱網上指南

您還可以將此身份驗證方法鏈接到現有用戶。例如,之前通過其他提供商(例如電話號碼)進行身份驗證的用戶可以將此登錄方法添加到其現有帳戶中。

不同之處在於操作的後半部分:

爪哇

// 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());
                }
            }
        });

科特林+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)
            }
        }

這也可用於在運行敏感操作之前重新驗證電子郵件鏈接用戶。

爪哇

// 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());
                }
            }
        });

科特林+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 。這對於標識符優先的流程很有用,在這種流程中,首先要求用戶提供他們的電子郵件,然後提供登錄方法:

爪哇

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());
                }
            }
        });

科特林+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對象。請參閱管理用戶

  • 在你的火力地堡實時數據庫和雲存儲安全規則,你可以得到簽署的,從用戶的唯一的用戶ID auth的變量,並用它來控制哪些數據的用戶可以訪問。

您可以允許用戶通過使用多個身份驗證提供登錄到您的應用程序連接身份驗證提供憑據到現有的用戶帳戶。

要註銷用戶,請撥打signOut

爪哇

FirebaseAuth.getInstance().signOut();

科特林+KTX

Firebase.auth.signOut()