在 Android 中使用電子郵件連結透過 Firebase 進行身份驗證

您可以使用 Firebase 驗證來登入用戶,方法是向用戶發送一封包含連結的電子郵件,用戶可以點擊該連結進行登入。在此過程中,也會驗證用戶的電子郵件地址。

透過電子郵件登入有很多好處:

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

在你開始之前

設定您的 Android 項目

  1. 如果您尚未將 Firebase 新增至您的 Android 專案中,請將其新增至您的 Android 專案中。

  2. 模組(應用程式層級)Gradle 檔案(通常<project>/<app-module>/build.gradle.kts<project>/<app-module>/build.gradle )中,新增Firebase 驗證的依賴項Android 的庫。我們建議使用Firebase Android BoM來控制函式庫版本控制。

    此外,作為設定 Firebase 驗證的一部分,您需要將 Google Play 服務 SDK 新增至您的應用程式。

    dependencies {
        // Import the BoM for the Firebase platform
        implementation(platform("com.google.firebase:firebase-bom:32.7.4"))
    
        // 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:21.0.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:22.3.1")
    // Also add the dependency for the Google Play services library and specify its version implementation("com.google.android.gms:play-services-auth:21.0.0")
    }
    正在尋找 Kotlin 特定的庫模組?2023 年 10 月(Firebase BoM 32.5.0)開始,Kotlin 和 Java 開發人員都可以依賴主庫模組(有關詳細信息,請參閱有關此計劃的常見問題解答)。

若要透過電子郵件連結登入用戶,您必須先為您的 Firebase 專案啟用電子郵件提供者和電子郵件連結登入方法:

  1. Firebase 控制台中,開啟「驗證」部分。
  2. 登入方法標籤上,啟用電子郵件/密碼提供者。請注意,必須啟用電子郵件/密碼登入才能使用電子郵件連結登入。
  3. 在同一部分中,啟用電子郵件連結(無密碼登入)登入方法。
  4. 按一下「儲存」

若要啟動身分驗證流程,請向使用者顯示介面,提示使用者提供電子郵件地址,然後呼叫sendSignInLinkToEmail請求 Firebase 將驗證連結傳送到使用者的電子郵件。

  1. 建構ActionCodeSettings對象,該對象為 Firebase 提供有關如何建構電子郵件連結的說明。設定以下欄位:

    • url :要嵌入的深層連結以及要傳遞的任何其他狀態。該連結的網域必須在 Firebase 控制台授權網域清單中列入白名單,並且可以透過前往登入方法標籤(驗證 -> 登入方法)找到該清單。如果用戶的裝置上未安裝該應用程式且無法安裝該應用程序,則該連結會將使用者重新導向至此 URL。
    • androidPackageNameIOSBundleId :在 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 的更多信息,請參閱電子郵件操作中的傳遞狀態部分。

  2. 詢問使用者的電子郵件。

  3. 將身分驗證連結傳送到使用者的電子郵件,並儲存使用者的電子郵件,以防使用者在同一裝置上完成電子郵件登入。

    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 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 應用程式配置正確,動態連結將從啟動器 Activity 開始重定向到您的應用程式。
    3. 如果您希望動態連結重新導向到特定活動,則需要在AndroidManifest.xml檔案中設定 Intent 過濾器。這可以透過在意圖篩選器中指定動態連結網域或電子郵件操作處理程序來完成。預設情況下,電子郵件操作處理程序託管在如下例所示的網域上:
      PROJECT_ID.firebaseapp.com/
    4. 注意事項:
      1. 不要指定您在 Intent 過濾器中的 actionCodeSettings 上設定的 URL。
      2. 建立動態連結網域時,您可能也建立了一個短 URL 連結。這個短網址不會被傳遞;不要配置您的 Intent 過濾器以使用android:pathPrefix屬性捕獲它。這意味著您將無法捕獲應用程式不同部分中的不同動態連結。但是,您可以檢查連結中的mode查詢參數以查看正在嘗試執行什麼操作,或使用 SDK 方法(例如isSignInWithEmailLink來查看您的應用程式收到的連結是否執行您想要的操作。
    5. 有關接收動態連結的更多信息,請參閱接收 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 的資訊。

如果您在 2023 年 9 月 15 日或之後建立項目,則預設啟用電子郵件枚舉保護。此功能提高了專案使用者帳戶的安全性,但它停用了fetchSignInMethodsForEmail()方法,我們先前建議使用該方法來實作識別碼優先的流程。

儘管您可以為項目停用電子郵件枚舉保護,但我們建議不要這樣做。

有關更多詳細信息,請參閱有關電子郵件枚舉保護的文件。

下一步

使用者首次登入後,系統會建立新的使用者帳戶,並將其連結到使用者登入時所使用的憑證(即使用者名稱和密碼、電話號碼或驗證提供者資訊)。此新帳戶將作為 Firebase 專案的一部分存儲,並且可用於識別專案中每個應用程式中的用戶,無論用戶如何登入。

  • 在您的應用程式中,您可以從FirebaseUser物件取得使用者的基本個人資料資訊。請參閱管理用戶

  • 在 Firebase 即時資料庫和雲端儲存安全性規則中,您可以從auth變數取得登入使用者的唯一使用者 ID,並使用它來控制使用者可以存取哪些資料。

您可以透過將身分驗證提供者憑證連結到現有使用者帳戶,允許使用者使用多個驗證提供者登入您的應用程式。

若要登出用戶,請呼叫signOut

Kotlin+KTX

Firebase.auth.signOut()

Java

FirebaseAuth.getInstance().signOut();