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

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

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

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

在你開始之前

  1. 如果您還沒有,請按照入門指南中的步驟操作。

  2. 為您的 Firebase 項目啟用電子郵件鏈接登錄。

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

    1. Firebase 控制台中,打開Auth部分。
    2. 登錄方法選項卡上,啟用電子郵件/密碼提供程序。請注意,必須啟用電子郵件/密碼登錄才能使用電子郵件鏈接登錄。
    3. 在同一部分中,啟用電子郵件鏈接(無密碼登錄)登錄方法。
    4. 單擊保存

要啟動身份驗證流程,請提供一個界面,提示用戶提供他們的電子郵件地址,然後調用sendSignInLinkToEmail()以請求 Firebase 將身份驗證鏈接發送到用戶的電子郵件。

  1. 構造 ActionCodeSettings 對象,該對象為 Firebase 提供有關如何構造電子郵件鏈接的說明。設置以下字段:

    • url :要嵌入的深層鏈接和要傳遞的任何其他狀態。鏈接的域必須在授權域的 Firebase 控制台列表中列入白名單,可以通過轉到登錄方法選項卡(身份驗證 -> 登錄方法)找到該列表。如果應用程序未安裝在用戶的設備上並且無法安裝該應用程序,該鏈接會將用戶重定向到此 URL。

    • androidPackageNameIOSBundleId :在 Android 或 iOS 設備上打開登錄鏈接時使用的應用程序。詳細了解如何配置 Firebase 動態鏈接以通過移動應用打開電子郵件操作鏈接。

    • handleCodeInApp :設置為true 。與其他帶外電子郵件操作(密碼重置和電子郵件驗證)不同,登錄操作必須始終在應用程序中完成。這是因為,在流程結束時,預計用戶會登錄,並且他們的身份驗證狀態會保留在應用程序中。

    • dynamicLinkDomain :當為一個項目定義了多個自定義動態鏈接域時,指定在通過指定的移動應用程序(例如example.page.link )打開鏈接時使用哪一個。否則將自動選擇第一個域。

    var acs = 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,
        iOSBundleId: 'com.example.ios',
        androidPackageName: 'com.example.android',
        // installIfNotAvailable
        androidInstallApp: true,
        // minimumVersion
        androidMinimumVersion: '12');
    
  2. 向用戶詢問他們的電子郵件。

  3. 將認證鏈接發送到用戶郵箱,並保存用戶郵箱,以防用戶在同一設備上完成郵箱登錄。

    var emailAuth = 'someemail@domain.com';
    FirebaseAuth.instance.sendSignInLinkToEmail(
            email: emailAuth, actionCodeSettings: acs)
        .catchError((onError) => print('Error sending email verification $onError'))
        .then((value) => print('Successfully sent email verification'));
    });
    

安全問題

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

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

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

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

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

  1. 指南中設置您的應用以接收 Flutter 上的動態鏈接。

  2. 在您的鏈接處理程序中,檢查該鏈接是否用於電子郵件鏈接身份驗證,如果是,請完成登錄過程。

    // Confirm the link is a sign-in with email link.
    if (FirebaseAuth.instance.isSignInWithEmailLink(emailLink)) {
      try {
        // The client SDK will parse the code from the link for you.
        final userCredential = await FirebaseAuth.instance
            .signInWithEmailLink(email: emailAuth, emailLink: emailLink);
    
        // You can access the new user via userCredential.user.
        final emailAddress = userCredential.user?.email;
    
        print('Successfully signed in with email link!');
      } catch (error) {
        print('Error signing in with email link.');
      }
    }
    

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

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

final authCredential = EmailAuthProvider
    .credentialWithLink(email: emailAuth, emailLink: emailLink.toString());
try {
    await FirebaseAuth.instance.currentUser
        ?.linkWithCredential(authCredential);
} catch (error) {
    print("Error linking emailLink credential.");
}

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

final authCredential = EmailAuthProvider
    .credentialWithLink(email: emailAuth, emailLink: emailLink.toString());
try {
    await FirebaseAuth.instance.currentUser
        ?.reauthenticateWithCredential(authCredential);
} catch (error) {
    print("Error reauthenticating credential.");
}

但是,由於流程可能最終在原始用戶未登錄的不同設備上結束,因此該流程可能無法完成。在這種情況下,可以向用戶顯示錯誤以強制他們在同一設備上打開鏈接。可以在鏈接中傳遞一些狀態以提供有關操作類型和用戶 uid 的信息。

如果您同時支持密碼和基於鏈接的電子郵件登錄,為了區分密碼/鏈接用戶的登錄方法,請使用fetchSignInMethodsForEmail 。這對於首先要求用戶提供其電子郵件然後向其顯示登錄方法的標識符優先流程很有用:

try {
    final signInMethods =
        await FirebaseAuth.instance.fetchSignInMethodsForEmail(emailAuth);
    final userExists = signInMethods.isNotEmpty;
    final canSignInWithLink = signInMethods
        .contains(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD);
    final canSignInWithPassword = signInMethods
        .contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD);
} on FirebaseAuthException catch (exception) {
    switch (exception.code) {
        case "invalid-email":
            print("Not a valid email address.");
            break;
        default:
            print("Unknown error.");
    }
}

如上所述,電子郵件/密碼和電子郵件/鏈接被視為具有不同登錄方法的相同EmailAuthProvider (相同的PROVIDER_ID )。

下一步

用戶創建新帳戶後,此帳戶將作為 Firebase 項目的一部分存儲,並可用於在項目中的每個應用中識別用戶,無論用戶使用何種登錄方法。

在您的應用程序中,您可以從User對像中獲取用戶的基本個人資料信息。請參閱管理用戶

在您的 Firebase 實時數據庫和雲存儲安全規則中,您可以從auth變量中獲取登錄用戶的唯一用戶 ID,並使用它來控制用戶可以訪問哪些數據。

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

要註銷用戶,請調用signOut()

await FirebaseAuth.instance.signOut();