在 Apple 平台上使用電子郵件連結透過 Firebase 進行身份驗證

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

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

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

在你開始之前

使用 Swift Package Manager 安裝和管理 Firebase 相依性。

  1. 在 Xcode 中,開啟應用程式項目,導覽至File > Add Packages
  2. 出現提示時,新增 Firebase Apple 平台 SDK 儲存庫:
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. 選擇 Firebase 身份驗證庫。
  5. -ObjC標誌新增至目標建置設定的「其他連結器標誌」部分。
  6. 完成後,Xcode 將自動開始在背景解析並下載您的依賴項。

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

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

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

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

    • url:要嵌入的深層連結以及要傳遞的任何其他狀態。該連結的網域必須在 Firebase 控制台授權網域清單中列入白名單,並且可以透過前往登入方法標籤(驗證 -> 登入方法)找到該清單。
    • iOSBundleID 和 androidPackageName :在 Android 或 Apple 裝置上開啟登入連結時要使用的應用程式。詳細了解如何配置 Firebase 動態連結以透過行動應用程式開啟電子郵件操作連結。
    • handleCodeInApp:設定為 true。與其他帶外電子郵件操作(密碼重設和電子郵件驗證)不同,登入操作必須始終在應用程式中完成。這是因為,在流程結束時,使用者應該登入並且他們的身份驗證狀態會保留在應用程式中。
    • DynamicLinkDomain:當為專案定義多個自訂動態連結網域時,指定透過指定的行動應用程式開啟連結時要使用哪一個(例如example.page.link )。否則將自動選擇第一個網域。

    迅速

    let actionCodeSettings = ActionCodeSettings()
    actionCodeSettings.url = URL(string: "https://www.example.com")
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
    actionCodeSettings.setAndroidPackageName("com.example.android",
                                             installIfNotAvailable: false, minimumVersion: "12")
    

    Objective-C

    FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc] init];
    [actionCodeSettings setURL:[NSURL URLWithString:@"https://www.example.com"]];
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = YES;
    [actionCodeSettings setIOSBundleID:[[NSBundle mainBundle] bundleIdentifier]];
    [actionCodeSettings setAndroidPackageName:@"com.example.android"
                        installIfNotAvailable:NO
                               minimumVersion:@"12"];
    

    要了解有關 ActionCodeSettings 的更多信息,請參閱電子郵件操作中的傳遞狀態部分。

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

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

    迅速

    Auth.auth().sendSignInLink(toEmail: email,
                               actionCodeSettings: actionCodeSettings) { error in
      // ...
        if let error = error {
          self.showMessagePrompt(error.localizedDescription)
          return
        }
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        UserDefaults.standard.set(email, forKey: "Email")
        self.showMessagePrompt("Check your email for link")
        // ...
    }
    

    Objective-C

    [[FIRAuth auth] sendSignInLinkToEmail:email
                       actionCodeSettings:actionCodeSettings
                               completion:^(NSError *_Nullable error) {
      // ...
        if (error) {
          [self showMessagePrompt:error.localizedDescription];
           return;
        }
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        [NSUserDefaults.standardUserDefaults setObject:email forKey:@"Email"];
        [self showMessagePrompt:@"Check your email for link"];
        // ...
    }];
    

安全問題

為了防止登入連結被用於以非預期使用者身分或在非預期裝置上登錄,Firebase Auth 要求在完成登入流程時提供使用者的電子郵件地址。為了成功登錄,此電子郵件地址必須與登入連結最初發送到的地址相符。

您可以透過在發送登入電子郵件時在本地儲存他們的電子郵件地址,為在請求連結的相同裝置上開啟登入連結的使用者簡化此流程。然後,使用這個位址來完成流程。

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

在 Apple 行動應用程式中完成登入

Firebase 驗證使用 Firebase 動態連結將電子郵件連結傳送到行動裝置。要透過行動應用程式完成登錄,必須將應用程式配置為檢測傳入的應用程式鏈接,解析底層深層鏈接,然後完成登入。

Firebase Auth 在傳送要在行動應用程式中開啟的連結時使用Firebase 動態連結。為了使用此功能,需要在 Firebase 控制台中配置動態連結。

  1. 啟用 Firebase 動態連結:

    1. Firebase 控制台中,開啟動態連結部分。
    2. 如果您尚未接受動態連結條款並建立動態連結網域,請立即執行。

      如果您已經建立了動態連結網域,請記下它。動態連結網域通常類似於以下範例:

      example.page.link

      當您配置 Apple 或 Android 應用程式以攔截傳入連結時,您將需要此值。

  2. 配置蘋果應用程式:

    1. 如果您打算從應用程式處理這些鏈接,則需要在 Firebase 控制台專案設定中指定捆綁包 ID。此外,還需要指定App Store ID和Apple Developer Team ID。
    2. 您還需要將電子郵件操作處理程序網域配置為應用程式功能中的關聯域。預設情況下,電子郵件操作處理程序託管在如下範例所示的網域上:
      APP_ID.firebaseapp.com
    3. 如果您打算將應用程式分發到 iOS 8 及以下版本,則需要將捆綁包 ID 設定為傳入 URL 的自訂方案。
    4. 有關詳細信息,請參閱接收 Apple 平台動態連結說明

收到上述連結後,請驗證該連結是否用於電子郵件連結驗證並完成登入。

迅速

if Auth.auth().isSignIn(withEmailLink: link) {
        Auth.auth().signIn(withEmail: email, link: self.link) { user, error in
          // ...
        }
}

Objective-C

if ([[FIRAuth auth] isSignInWithEmailLink:link]) {
    [[FIRAuth auth] signInWithEmail:email
                               link:link
                         completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
      // ...
    }];
}

若要了解如何在 Android 應用程式中使用電子郵件連結處理登錄,請參閱Android 指南

若要了解如何在 Web 應用程式中使用電子郵件連結處理登錄,請參閱Web 指南

您也可以將此身份驗證方法連結到現有使用者。例如,先前透過其他提供者(例如電話號碼)進行身份驗證的使用者可以將此登入方法新增至其現有帳戶。

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

迅速

  let credential = EmailAuthCredential.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.link(with: credential) { authData, error in
    if (error) {
      // And error occurred during linking.
      return
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthProvider credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      linkWithCredential:credential
              completion:^(FIRAuthDataResult *_Nullable result,
                           NSError *_Nullable error) {
    if (error) {
      // And error occurred during linking.
      return;
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }];

這也可用於在執行敏感操作之前重新驗證電子郵件連結使用者。

迅速

  let credential = EmailAuthProvider.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.reauthenticate(with: credential) { authData, error in
    if (error) {
      // And error occurred during re-authentication.
      return
    }
    // The user was successfully re-authenticated.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthCredential credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      reauthenticateWithCredential:credential
                        completion:^(FIRAuthDataResult *_Nullable result,
                                     NSError *_Nullable error) {
    if (error) {
      // And error occurred during re-authentication
      return;
    }
    // The user was successfully re-authenticated.
  }];

但是,由於該流程可能最終到達原始使用者未登入的其他設備,因此該流程可能無法完成。在這種情況下,可以向使用者顯示錯誤,迫使他們在同一台裝置上開啟連結。可以在連結中傳遞一些狀態以提供有關操作類型和使用者 uid 的資訊。

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

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

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

下一步

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

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

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

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

若要登出用戶,請呼叫signOut:

迅速

let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}

Objective-C

NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

您可能還需要為所有身份驗證錯誤新增錯誤處理代碼。請參閱處理錯誤