使用电子邮件链接进行 Firebase 身份验证(Apple 平台)

借助 Firebase Authentication,您可以向用户发送包含链接的电子邮件,让用户点击此链接进行登录。在此过程中,用户的电子邮件地址也会得到验证。

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

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

准备工作

使用 Swift Package Manager 安装和管理 Firebase 依赖项。

  1. 在 Xcode 中打开您的应用项目,依次点击 File(文件)> Add Packages(添加软件包)
  2. 出现提示时,添加 Firebase Apple 平台 SDK 代码库:
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. 选择 Firebase Authentication 库。
  5. -ObjC 标志添加到目标 build 设置的“其他链接器标志”部分。
  6. 完成之后,Xcode 将会自动开始在后台解析和下载您的依赖项。

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

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

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

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

    • url:要嵌入的深层链接以及要传递的其他任何状态。您必须在 Firebase 控制台的“已获授权的网域”列表中添加链接的网域。您可以在“登录方法”标签页(“Authentication”->“登录方法”)中找到这些网域。
    • iOSBundleIDandroidPackageName:可帮助 Firebase Authentication 确定是否应创建在 Android 或 Apple 设备上打开的仅限网页链接或移动链接。
    • handleCodeInApp:设置为 true。与其他带外电子邮件操作(密码重置和电子邮件验证)不同,登录操作必须始终在应用中完成。这是因为按照预期,在流程结束时用户应该已经登录,并且他们的身份验证状态应在应用内持久保存。
    • linkDomain:如果为项目定义了自定义 Hosting 链接网域,请指定在通过指定的移动应用打开链接时应使用哪个网域。否则,系统会自动选择默认网域(例如 PROJECT_ID.firebaseapp.com)。
    • dynamicLinkDomain:已弃用。请勿指定此参数。

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

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

登录完成后,用户之前未经过验证的登录机制都将被移除,且任何现有会话都将失效。例如,如果之前有人使用相同的电子邮件地址和密码创建了一个未经验证的账号,系统会移除其密码,以防之前冒名顶替并创建了该账号的人再次使用未验证的账号登录。

在 Apple 移动应用中完成登录

Firebase Authentication 使用 Firebase Hosting 将电子邮件链接发送到移动设备。为了通过移动应用完成登录,必须对应用进行配置,使其检测传入的应用链接,解析底层的深层链接,然后完成登录。如需详细了解如何完成此操作,请参阅 iOS 上的通用链接和关联的网域

配置 Firebase Hosting

在创建和发送要在移动应用中打开的链接时,Firebase Authentication 会使用 Firebase Hosting 网域。系统已为您配置默认的 Firebase Hosting 网域。

  1. 配置 Firebase Hosting 网域:

    Firebase 控制台中,打开 Hosting 部分。

    • 如果您想为移动应用中打开的电子邮件链接使用默认网域,请前往默认网站,并记下默认的 Hosting 网域。默认的 Hosting 网域通常如下所示:PROJECT_ID.firebaseapp.com

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

    • 如果您想为电子邮件链接使用自定义网域,可以Firebase Hosting 注册一个网域,并将其用作链接的网域。

  2. 配置 Apple 应用:

    您需要将所选网域配置为应用链接的关联网域。如需在应用中设置授权,请在 Xcode 中打开目标平台的 Signing & Capabilities 标签页,然后将上一步中的 Firebase Hosting 网域添加到关联的网域功能。如果使用默认的 Firebase Hosting 网域,则将为 applinks:PROJECT_ID.firebaseapp.com

    如需了解详情,请参阅 Apple 文档网站上的支持关联的网域

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

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

如需了解如何处理在 Web 应用中使用电子邮件链接进行的登录,请参阅 Web 指南

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

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

Swift

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

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

Swift

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

Firebase Authentication iOS SDK 11.8.0 之前,电子邮件链接登录功能依赖于 Firebase Dynamic Links 在正确的应用中打开登录链接。这些验证链接已弃用,因为 Firebase Dynamic Links于 2025 年 8 月 25 日关停

如果您的应用使用旧样式链接,您应将应用迁移到基于 Firebase Hosting 的新系统。

如果您的项目是在 2023 年 9 月 15 日当天或之后创建,则默认启用了电子邮件枚举保护功能。此功能可提高项目的用户账号的安全性,但会停用 fetchSignInMethodsForEmail() 方法,我们之前建议使用该方法来实现标识符优先流程。

虽然您可以为项目停用电子邮件枚举保护,但我们不建议您这样做。

如需了解详情,请参阅启用或停用电子邮件枚举保护

后续步骤

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

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

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

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