使用電子郵件連結向 Firebase 驗證

您可以使用 Firebase 驗證功能,傳送含有連結的電子郵件給使用者,讓他們點選連結登入。在這個過程中,使用者的電子郵件地址也會經過驗證。

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

  • 註冊和登入流程簡便。
  • 降低跨應用程式重複使用密碼的風險,這可能會破壞精心挑選的密碼安全性。
  • 驗證使用者身分,同時確認使用者是否為電子郵件地址的合法擁有者。
  • 使用者只需要具備可存取的電子郵件帳戶即可登入。不必擁有電話號碼或社群媒體帳戶。
  • 使用者可以安全登入,而無須提供 (或記住) 密碼,這在行動裝置上可能會很麻煩。
  • 如果使用者先前是使用電子郵件 ID (密碼或聯合登入) 登入,則可升級為只使用電子郵件登入。舉例來說,如果使用者忘記密碼,仍可登入帳戶,無須重設密碼。

事前準備

  1. 如果尚未開始,請按照「開始使用」指南中的步驟操作。

  2. 為 Firebase 專案啟用電子郵件連結登入功能。

    如要透過電子郵件連結登入使用者,您必須先為 Firebase 專案啟用電子郵件供應器和電子郵件連結登入方式:

    1. Firebase 控制台中,開啟「驗證」部分。
    2. 在「Sign in method」分頁中,啟用「Email/Password」提供者。請注意,您必須啟用電子郵件/密碼登入功能,才能使用電子郵件連結登入功能。
    3. 在同一個部分中,啟用「電子郵件連結 (不需要密碼即可登入)」登入方式。
    4. 按一下 [儲存]

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

  1. 建構 ActionCodeSettings 物件,為 Firebase 提供如何建構電子郵件連結的指示。設定下列欄位:

    • url:要嵌入的深層連結,以及要傳遞的任何其他狀態。連結的網域必須列於 Firebase 控制台的授權網域清單中,您可以前往「設定」分頁 (「驗證」->「設定」->「授權網域」) 查看。如果使用者的裝置上未安裝應用程式,且無法安裝應用程式,連結會將使用者重新導向至這個網址。

    • androidPackageNameIOSBundleId:在 Android 或 iOS 裝置上開啟登入連結時要使用的應用程式。進一步瞭解如何設定 Firebase Dynamic Links,透過行動應用程式開啟電子郵件動作連結。

    • handleCodeInApp:設為 true。與其他非頻道電子郵件操作 (密碼重設和電子郵件驗證) 不同,登入作業必須一律在應用程式中完成。這是因為在流程結束時,使用者應已登入,且其驗證狀態會在應用程式中保留。

    • dynamicLinkDomain:(已淘汰,請改用 linkDomain) 為專案定義多個自訂動態連結網域時,請指定使用指定行動應用程式 (例如 example.page.link) 開啟連結時要使用的網域。否則,系統會自動選取第一個網域。

    • linkDomain:使用指定行動應用程式開啟連結時,可選用自訂 Firebase 託管網域。網域必須在 Firebase 託管中設定,且由專案擁有。此網域不得為預設的代管網域 (web.appfirebaseapp.com)。這項設定取代已淘汰的 dynamicLinkDomain 設定。

    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 Auth 要求使用者在完成登入流程時提供電子郵件地址。如要順利登入,這個電子郵件地址必須與登入連結原本傳送的地址相符。

您可以為在要求連結時使用的相同裝置上開啟登入連結的使用者,在傳送登入電子郵件時,將電子郵件地址儲存在本機 (例如使用 SharedPreferences),藉此簡化這項流程。然後使用這個地址完成流程。請勿在重新導向網址參數中傳遞使用者的電子郵件地址,並重複使用該地址,因為這可能會啟用工作階段注入。

登入完成後,系統會從使用者移除先前未經驗證的登入機制,並將所有現有工作階段設為無效。舉例來說,如果有人先前使用相同的電子郵件地址和密碼建立未經驗證的帳戶,系統會移除使用者的密碼,以防冒用者聲稱擁有該帳戶,並使用未經驗證的電子郵件地址和密碼再次登入。

此外,請務必在實際工作環境中使用 HTTPS 網址,以免連結遭到中繼伺服器攔截。

完成登入

Firebase Dynamic Links 已淘汰,現在使用 Firebase 代管服務傳送登入連結。請按照指南設定特定平台:

如要透過行動應用程式完成登入程序,應用程式必須設定為偵測傳入的應用程式連結、剖析底層深層連結,然後完成登入程序。

  1. 在連結處理常式中,檢查連結是否用於電子郵件連結驗證,如果是的話,請完成登入程序。

    // 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 的相關資訊。

如果您是在 2023 年 9 月 15 日當天或之後建立專案,系統會預設啟用電子郵件列舉保護功能。這項功能可提升專案使用者帳戶的安全性,但會停用 fetchSignInMethodsForEmail() 方法,我們先前曾建議使用這項方法實作 ID 優先流程。

雖然您可以為專案停用電子郵件列舉防護,但我們不建議這麼做。

詳情請參閱電子郵件列舉防護功能的說明文件。

後續步驟

使用者建立新帳戶後,系統會將該帳戶儲存在 Firebase 專案中,並可用於在專案中的每個應用程式中識別使用者,無論使用者採用何種登入方式皆然。

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

在 Firebase 即時資料庫和 Cloud Storage 安全性規則中,您可以從 auth 變數取得已登入使用者的專屬使用者 ID,並利用該 ID 控制使用者可存取的資料。

您可以將驗證服務供應商憑證連結至現有使用者帳戶,讓使用者使用多個驗證服務供應商登入應用程式。

如要將使用者登出,請呼叫 signOut()

await FirebaseAuth.instance.signOut();