在 Apple 平台上使用電子郵件鏈接通過 Firebase 進行身份驗證

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

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

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

在你開始之前

使用 Swift Package Manager 安裝和管理 Firebase 依賴項。

  1. 在 Xcode 中,打開您的應用項目,導航到File > Add Packages
  2. 出現提示時,添加 Firebase Apple 平台 SDK 存儲庫:
  3.   https://github.com/firebase/firebase-ios-sdk
  4. 選擇 Firebase 身份驗證庫。
  5. 完成後,Xcode 將在後台自動開始解析和下載您的依賴項。

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

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

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

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

    • url:要嵌入的深層鏈接和要傳遞的任何其他狀態。鏈接的域必須在授權域的 Firebase 控制台列表中列入白名單,可以通過轉到登錄方法選項卡(身份驗證 -> 登錄方法)找到該列表。
    • iOSBundleID 和 androidPackageName :在 Android 或 Apple 設備上打開登錄鏈接時使用的應用程序。詳細了解如何配置 Firebase 動態鏈接以通過移動應用打開電子郵件操作鏈接。
    • handleCodeInApp:設置為真。與其他帶外電子郵件操作(密碼重置和電子郵件驗證)不同,登錄操作必須始終在應用程序中完成。這是因為,在流程結束時,預計用戶會登錄,並且他們的身份驗證狀態會保留在應用程序中。
    • 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 身份驗證要求在完成登錄流程時提供用戶的電子郵件地址。要成功登錄,此電子郵件地址必須與最初發送登錄鏈接的地址相匹配。

您可以為在請求鏈接的同一設備上打開登錄鏈接的用戶簡化此流程,方法是在您發送登錄電子郵件時將他們的電子郵件地址存儲在本地。然後,使用此地址完成流程。

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

在 Apple 移動應用程序中完成登錄

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

Firebase Auth 在發送要在移動應用程序中打開的鏈接時使用Firebase 動態鏈接。要使用此功能,需要在 Firebase 控制台中配置動態鏈接。

  1. 啟用 Firebase 動態鏈接:

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

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

      example.page.link

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

  2. 配置 Apple 應用程序:

    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 的信息。

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

迅速

 // After asking the user for their email.
 Auth.auth().fetchSignInMethods(forEmail: email) { signInMethods, error in
   // This returns the same array as fetchProviders(forEmail:completion:) but for email
   // provider identified by 'password' string, signInMethods would contain 2
   // different strings:
   // 'emailLink' if the user previously signed in with an email/link
   // 'password' if the user has a password.
   // A user could have both.
   if (error) {
     // Handle error case.
   }
   if (!signInMethods.contains(EmailPasswordAuthSignInMethod)) {
     // User can sign in with email/password.
   }
   if (!signInMethods.contains(EmailLinkAuthSignInMethod)) {
     // User can sign in with email/link.
   }
 }

Objective-C

 // After asking the user for their email.
 [FIRAuth auth] fetchSignInMethodsForEmail:email
                                completion:^(NSArray *_Nullable signInMethods,
                                             NSError *_Nullable error) {
   // This returns the same array as fetchProvidersForEmail but for email
   // provider identified by 'password' string, signInMethods would contain 2
   // different strings:
   // 'emailLink' if the user previously signed in with an email/link
   // 'password' if the user has a password.
   // A user could have both.
   if (error) {
     // Handle error case.
   }
   if (![signInMethods containsObject:FIREmailPasswordAuthSignInMethod]) {
     // User can sign in with email/password.
   }
   if (![signInMethods containsObject:FIREmailLinkAuthSignInMethod]) {
     // User can sign in with email/link.
   }
 }];

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

下一步

用戶首次登錄後,會創建一個新用戶帳戶並將其鏈接到憑據(即用戶名和密碼、電話號碼或身份驗證提供商信息),即用戶登錄時使用的憑據。這個新帳戶作為 Firebase 項目的一部分存儲,可用於在項目中的每個應用中識別用戶,無論用戶如何登錄。

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

  • 在您的 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;
}

您可能還想為所有身份驗證錯誤添加錯誤處理代碼。請參閱處理錯誤