在 iOS 中使用电子邮件链接进行 Firebase 身份验证

借助 Firebase 身份验证,您可以通过以下方式让用户登录:向用户发送包含链接的电子邮件,用户点击链接进行登录。在此过程中,系统也会验证用户的电子邮件地址。

通过电子邮件登录的好处有很多:

  • 流畅的注册和登录过程。
  • 降低跨应用重复使用密码的风险。即使是精心挑选的密码,如果重复使用,安全性也会受到影响。
  • 在验证用户身份的同时,验证用户是否为电子邮件地址的合法所有者。
  • 用户只需一个可访问的电子邮件帐号即可登录,不需要拥有电话号码或社交媒体帐号。
  • 用户可以安全地登录,而无需提供(或记住)密码。在移动设备上,输密码可能是件麻烦事。
  • 以前使用电子邮件标识符(密码或联合登录服务)登录的现有用户可以升级为仅使用电子邮件登录。例如,用户即使忘记密码也仍可以登录,无需重置密码。

开始之前

  1. 将 Firebase 添加到您的 iOS 项目。在您的 Podfile 中添加以下 pod:
    pod 'Firebase/Auth'
    
  2. 如果您尚未将您的应用与 Firebase 项目相关联,请在 Firebase 控制台中进行关联。

要让用户能够通过电子邮件链接登录,您必须先为 Firebase 项目启用电子邮件提供方和电子邮件链接登录方法,操作步骤如下:

  1. Firebase 控制台中,打开 Authentication(身份验证)部分。
  2. 登录方法标签中,启用电子邮件地址/密码提供方。请注意,必须启用电子邮件/密码登录才能使用电子邮件链接登录。
  3. 在同一部分中,启用电子邮件链接(无密码登录)登录方法。
  4. 点击保存

要启动身份验证流程,请向用户显示一个要求其提供电子邮件地址的界面,然后调用 sendSignInLinkToEmail:actionCodeSettings:completion: 以请求 Firebase 发送身份验证链接到用户的电子邮件地址。

  1. 构建 ActionCodeSettings 对象,以向 Firebase 提供有关如何构建电子邮件链接的说明。设置以下字段:

    • url:要嵌入的深层链接以及要传递的任何其他状态。您必须在 Firebase 控制台的“已获授权的网域”列表中将链接的网域列入白名单。要找到这些网域,您可以转到“登录方法”标签(“身份验证”->“登录方法”)。
    • iOSBundleID 和 androidPackageName:在 Android 或 iOS 设备上打开登录链接时使用的应用。详细了解如何配置 Firebase 动态链接以通过移动应用打开电子邮件操作链接。
    • handleCodeInApp:设为 true。与其他带外电子邮件操作(密码重置和电子邮件验证)不同,登录操作必须始终在应用中完成。这是因为按照预想,在流程结束时用户应该已经登录,并且他们的身份验证状态应在应用内保持不变。

    Swift

    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. 向用户的电子邮件地址发送身份验证链接,并保存用户的电子邮件地址,因为用户可能在同一设备上完成电子邮件登录。

    Swift

    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 身份验证功能会要求在完成登录流程时提供用户的电子邮件地址。要成功登录,此电子邮件地址必须与登录链接最初发送到的地址相一致。

您可以为在请求链接的同一设备上打开登录链接的用户简化此流程,方法是在发送登录电子邮件时于本地存储他们的电子邮件地址。然后,使用这个地址来完成流程。

登录完成后,用户以前未验证过的登录机制都将移除,现有会话将失效。例如,如果有人在以前使用相同的电子邮件地址和密码创建了一个未经验证的帐号,系统会移除用户的密码,以防止认领了所有权并创建了该未验证帐号的冒名者再次使用该帐号登录。

在 iOS 应用中完成登录

Firebase 身份验证功能使用 Firebase 动态链接将电子邮件链接发送到移动设备上。要通过移动应用完成登录,必须对应用进行配置,使其检测传入的应用链接,解析底层的深层链接,然后完成登录。

在发送要在移动应用中打开的链接时,Firebase 身份验证会使用 Firebase 动态链接。要使用此功能,您需要在 Firebase 控制台中配置动态链接。

  1. 启用 Firebase 动态链接:

    1. Firebase 控制台中,打开 Dynamic Links(动态链接)部分。
    2. 如果您尚未接受动态链接条款,并且也未创建动态链接网域,请立即完成这些操作。

      如果您已经创建了动态链接网域,请记下它。动态链接网域通常采用如下格式:

      example.page.link

      配置 iOS 或 Android 应用以拦截传入链接时,您需要用到此值。

  2. 配置 iOS 应用:

    1. 如果您计划在 iOS 应用中处理这些链接,则需要在 Firebase 控制台项目设置中指定 iOS 软件包 ID。此外,您还需要指定 App Store ID 和 Apple Developer Team ID。
    2. 您还需要在应用功能中将 FDL 通用链接网域配置为关联网域。
    3. 如果您计划将应用分发到 iOS 8 及更低版本,则需要将 iOS 软件包 ID 设置为传入网址的自定义架构。
    4. 如需了解详情,请参阅有关如何接收 iOS 动态链接的说明。

在按上述说明收到链接后,验证链接是否适用于电子邮件链接身份验证并完成登录。

Swift

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 指南

要了解如何在网页应用中处理使用电子邮件地址登录的链接,请参阅网页指南

您还可以将此身份验证方法与现有用户相关联。例如,以前使用其他提供方(例如电话号码)进行身份验证的用户,可以将此登录方法添加到其现有帐号。

不同之处在于操作的后半部分:

Swift

  let credential = EmailAuthCredential.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.linkAndRetrieveData(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 =
      [FIREmailAuthCredential credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      linkAndRetrieveDataWithCredential: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.
  }];

这也可以用于在执行敏感操作之前重新验证电子邮件链接用户的身份。

Swift

  let credential = EmailAuthCredential.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.reauthenticateAndRetrieveData(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
      reauthenticateAndRetrieveDataWithCredential: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。这对于标识符优先流程(首先要求用户提供他们的电子邮件地址,然后向用户显示登录方法)非常有用:

Swift

 // 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.
   }
 }];

如上所述,电子邮件/密码和电子邮件/链接被认为是使用不同登录方法的相同 FIREmailAuthProviderPROVIDER_ID 相同)。

后续步骤

在用户首次登录后,系统会创建一个新的用户帐号,并将其与该用户登录时使用的凭据(即用户名和密码、电话号码或者身份验证提供方信息)相关联。此新帐号存储在您的 Firebase 项目中,您项目中的每个应用都可以使用此帐号来识别用户,无论用户采用何种方式登录。

  • 在您的应用中,您可以从 FIRUser 对象获取用户的个人资料基本信息。请参阅管理用户

  • 在您的 Firebase 实时数据库和 Cloud Storage 安全规则中,您可以从 auth 变量获取已登录用户的唯一身份用户 ID,然后用此 ID 来控制用户可以访问哪些数据。

您可以通过将身份验证提供方凭据关联至现有用户帐号,让用户可以使用多个身份验证提供方服务登录您的应用。

要让用户退出帐号,可调用 signOut:

Swift

    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;
}

您可能还需要添加适用于所有身份验证错误的错误处理代码。请参阅处理错误

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面