获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

向您的 iOS 应用程序添加多因素身份验证

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

如果您已使用 Identity Platform 升级到 Firebase 身份验证,则可以将 SMS 多重身份验证添加到您的 iOS 应用。

多因素身份验证可提高应用程序的安全性。虽然攻击者经常破坏密码和社交帐户,但拦截短信更加困难。

在你开始之前

  1. 启用至少一个支持多重身份验证的提供程序。电话验证、匿名验证和 Apple Game Center 外,每个提供商都支持 MFA。

  2. 确保您的应用正在验证用户电子邮件。 MFA 需要电子邮件验证。这可以防止恶意行为者使用他们不拥有的电子邮件注册服务,然后通过添加第二个因素锁定真正的所有者。

启用多重身份验证

  1. 打开 Firebase 控制台的身份验证 > 登录方法页面。

  2. 高级部分,启用SMS Multi-factor Authentication

    您还应该输入用于测试应用程序的电话号码。虽然可选,但强烈建议注册测试电话号码以避免在开发过程中受到限制。

  3. 如果您尚未授权应用的域,请将其添加到 Firebase 控制台的“身份验证”>“设置”页面上的允许列表中。

验证您的应用

Firebase 需要验证 SMS 请求是否来自您的应用。您可以通过两种方式做到这一点:

  • 静默 APNs 通知:当您首次登录用户时,Firebase 可以向用户的设备发送静默推送通知。如果应用收到通知,则可以继续进行身份验证。请注意,从 iOS 8.0 开始,您无需要求用户允许推送通知使用此方法。

  • reCAPTCHA 验证:如果您无法发送静默通知(例如,因为用户禁用了后台刷新,或者您正在 iOS 模拟器中测试您的应用),您可以使用 reCAPTCHA。在许多情况下,reCAPTCHA 会自动解决,无需用户交互。

使用静默通知

要启用 APNs 通知以与 Firebase 一起使用:

  1. 在 Xcode 中,为您的项目启用推送通知

  2. 使用 Firebase 控制台上传您的 APNs 身份验证密钥(您的更改将自动转移到 Google Cloud Firebase)。如果您还没有 APNs 身份验证密钥,请参阅使用 FCM 配置 APNs以了解如何获取它。

    1. 打开Firebase 控制台

    2. 导航到项目设置

    3. 选择云消息选项卡。

    4. APNs authentication key下,在iOS app configuration部分中,单击Upload

    5. 选择您的密钥。

    6. 为密钥添加密钥 ID。您可以在Apple Developer Member CenterCertificates, Identifiers & Profiles下找到密钥 ID。

    7. 点击上传

如果您已经拥有 APNs 证书,则可以改为上传证书。

使用 reCAPTCHA 验证

要使客户端 SDK 使用 reCAPTCHA:

  1. 在 Xcode 中打开您的项目配置。

  2. 双击左侧树视图中的项目名称。

  3. 从“目标”部分中选择您的应用。

  4. 选择信息选项卡。

  5. 展开URL 类型部分。

  6. 单击+按钮。

  7. URL Schemes字段中输入您的反向客户端 ID。您可以在GoogleService-Info.plist配置文件中找到该值作为REVERSED_CLIENT_ID列出。

完成后,您的配置应类似于以下内容:

自定义方案

或者,您可以自定义应用程序在显示 reCAPTCHA 时呈现SFSafariViewControllerUIWebView的方式。为此,请创建一个符合FIRAuthUIDelegate协议的自定义类,并将其传递给verifyPhoneNumber:UIDelegate:completion:

选择注册模式

您可以选择您的应用程序是否需要多重身份验证,以及注册用户的方式和时间。一些常见的模式包括:

  • 注册用户的第二个因素作为注册的一部分。如果您的应用需要对所有用户进行多重身份验证,请使用此方法。请注意,帐户必须具有经过验证的电子邮件地址才能注册第二个因素,因此您的注册流程必须适应这一点。

  • 提供可跳过的选项以在注册期间注册第二个因素。想要鼓励但不要求多因素身份验证的应用程序可能更喜欢这种方法。

  • 提供从用户帐户或配置文件管理页面添加第二个因素的能力,而不是注册屏幕。这最大限度地减少了注册过程中的摩擦,同时仍然为安全敏感的用户提供多因素身份验证。

  • 当用户想要访问具有更高安全要求的功能时,需要逐步添加第二个因素。

注册第二个因素

为用户注册一个新的次要因素:

  1. 重新验证用户。

  2. 要求用户输入他们的电话号码。

  3. 为用户获取多因素会话:

    迅速

    authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in
      // ...
    }
    

    Objective-C

    [authResult.user.multiFactor
      getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                NSError * _Nullable error) {
        // ...
    }];
    
  4. 向用户的手机发送验证消息。确保电话号码的格式以+开头,并且没有其他标点符号或空格(例如: +15105551234

    迅速

    // Send SMS verification code.
    PhoneAuthProvider.provider().verifyPhoneNumber(
      phoneNumber,
      uiDelegate: nil,
      multiFactorSession: session) { (verificationId, error) in
        // verificationId will be needed for enrollment completion.
    }
    

    Objective-C

    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider verifyPhoneNumber:phoneNumber
                                          UIDelegate:nil
                                  multiFactorSession:session
                                          completion:^(NSString * _Nullable verificationID,
                                                        NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.
    }];
    

    虽然不是必需的,但最好提前通知用户他们将收到 SMS 消息,并且适用标准费率。

    verifyPhoneNumber()方法使用静默推送通知在后台启动应用程序验证过程。如果静默推送通知不可用,则会发出 reCAPTCHA 质询。

  5. 发送 SMS 代码后,要求用户验证代码。然后,使用他们的响应来构建PhoneAuthCredential

    迅速

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: verificationCode)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider
                                           credentialWithVerificationID:verificationID
                                           verificationCode:kPhoneSecondFactorVerificationCode];
    
  6. 初始化一个断言对象:

    迅速

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  7. 完成报名。或者,您可以指定第二个因素的显示名称。这对于具有多个第二因素的用户很有用,因为电话号码在身份验证流程中被屏蔽(例如,+1******1234)。

    迅速

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
    

    Objective-C

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    [authResult.user.multiFactor enrollWithAssertion:assertion
                                         displayName:nil
                                          completion:^(NSError * _Nullable error) {
        // ...
    }];
    

下面的代码显示了注册第二个因素的完整示例:

迅速

let user = Auth.auth().currentUser
user?.multiFactor.getSessionWithCompletion({ (session, error) in
  // Send SMS verification code.
  PhoneAuthProvider.provider().verifyPhoneNumber(
    phoneNumber,
    uiDelegate: nil,
    multiFactorSession: session
  ) { (verificationId, error) in
    // verificationId will be needed for enrollment completion.
    // Ask user for the verification code.
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: phoneSecondFactorVerificationCode)
    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user?.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
  }
})

Objective-C

FIRUser *user = FIRAuth.auth.currentUser;
[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                              NSError * _Nullable error) {
    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumber:phoneNumber
      UIDelegate:nil
      multiFactorSession:session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.

        // Ask user for the verification code.
        // ...

        // Then:
        FIRPhoneAuthCredential *credential =
            [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID
                                                        verificationCode:kPhoneSecondFactorVerificationCode];
        FIRMultiFactorAssertion *assertion =
            [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

        // Complete enrollment. This will update the underlying tokens
        // and trigger ID token change listener.
        [user.multiFactor enrollWithAssertion:assertion
                                  displayName:displayName
                                    completion:^(NSError * _Nullable error) {
            // ...
        }];
    }];
}];

恭喜!您成功为用户注册了第二个身份验证因素。

使用第二个因素让用户登录

要使用双因素 SMS 验证登录用户:

  1. 使用他们的第一个因素登录用户,然后捕获指示需要多因素身份验证的错误。此错误包含一个解析器、关于已注册的第二个因素的提示,以及一个证明用户成功通过第一个因素进行身份验证的基础会话。

    例如,如果用户的第一个因素是电子邮件和密码:

    迅速

    Auth.auth().signIn(
      withEmail: email,
      password: password
    ) { (result, error) in
      let authError = error as NSError
      if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
        // The user is a multi-factor user. Second factor challenge is required.
        let resolver =
          authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
        // ...
      } else {
        // Handle other errors such as wrong password.
      }
    }
    

    Objective-C

    [FIRAuth.auth signInWithEmail:email
                         password:password
                       completion:^(FIRAuthDataResult * _Nullable authResult,
                                    NSError * _Nullable error) {
        if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
            // User is not enrolled with a second factor and is successfully signed in.
            // ...
        } else {
            // The user is a multi-factor user. Second factor challenge is required.
        }
    }];
    

    如果用户的第一个因素是联合提供程序,例如 OAuth,则在调用getCredentialWith()后捕获错误。

  2. 如果用户注册了多个次要因素,请询问他们使用哪一个。您可以使用 resolver.hints[selectedIndex].phoneNumber 获取被屏蔽的电话号码,使用resolver.hints[selectedIndex].phoneNumber resolver.hints[selectedIndex].displayName显示名称。

    迅速

    // Ask user which second factor to use. Then:
    if resolver.hints[selectedIndex].factorID == PhoneMultiFactorID {
      // User selected a phone second factor.
      // ...
    } else {
      // Unsupported second factor.
      // Note that only phone second factors are currently supported.
    }
    

    Objective-C

    FIRMultiFactorResolver *resolver =
        (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
    
    // Ask user which second factor to use. Then:
    FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];
    if (hint.factorID == FIRPhoneMultiFactorID) {
      // User selected a phone second factor.
      // ...
    } else {
      // Unsupported second factor.
      // Note that only phone second factors are currently supported.
    }
    
  3. 向用户手机发送验证信息:

    迅速

    // Send SMS verification code.
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      // verificationId will be needed for sign-in completion.
    }
    

    Objective-C

    // Send SMS verification code
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumberWithMultiFactorInfo:hint
      UIDelegate:nil
      multiFactorSession:resolver.session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        if (error != nil) {
            // Failed to verify phone number.
        }
    }];
    
  4. 发送 SMS 代码后,要求用户验证代码并使用它来构建PhoneAuthCredential

    迅速

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: verificationCodeFromUser)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential =
        [FIRPhoneAuthProvider.provider
          credentialWithVerificationID:verificationID
                      verificationCode:verificationCodeFromUser];
    
  5. 使用凭证初始化一个断言对象:

    迅速

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion =
        [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  6. 解决登录问题。然后,您可以访问原始登录结果,其中包括标准的提供商特定数据和身份验证凭据:

    迅速

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(with: assertion) { (authResult, error) in
      // authResult will also contain the user, additionalUserInfo, optional
      // credential (null for email/password) associated with the first factor sign-in.
    
      // For example, if the user signed in with Google as a first factor,
      // authResult.additionalUserInfo will contain data related to Google provider that
      // the user signed in with.
    
      // user.credential contains the Google OAuth credential.
      // user.credential.accessToken contains the Google OAuth access token.
      // user.credential.idToken contains the Google OAuth ID token.
    }
    

    Objective-C

    // Complete sign-in.
    [resolver resolveSignInWithAssertion:assertion
                              completion:^(FIRAuthDataResult * _Nullable authResult,
                                            NSError * _Nullable error) {
        if (error != nil) {
            // User successfully signed in with the second factor phone number.
        }
    }];
    

下面的代码显示了一个多因素用户登录的完整示例:

迅速

Auth.auth().signIn(
  withEmail: email,
  password: password
) { (result, error) in
  let authError = error as NSError?
  if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
    let resolver =
      authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver

    // Ask user which second factor to use.
    // ...

    // Then:
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo

    // Send SMS verification code
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      if error != nil {
        // Failed to verify phone number.
      }
      // Ask user for the SMS verification code.
      // ...

      // Then:
      let credential = PhoneAuthProvider.provider().credential(
        withVerificationID: verificationId!,
        verificationCode: verificationCodeFromUser)
      let assertion = PhoneMultiFactorGenerator.assertion(with: credential)

      // Complete sign-in.
      resolver.resolveSignIn(with: assertion) { (authResult, error) in
        if error != nil {
          // User successfully signed in with the second factor phone number.
        }
      }
    }
  }
}

Objective-C

[FIRAuth.auth signInWithEmail:email
                     password:password
                   completion:^(FIRAuthDataResult * _Nullable authResult,
                               NSError * _Nullable error) {
    if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
        // User is not enrolled with a second factor and is successfully signed in.
        // ...
    } else {
        FIRMultiFactorResolver *resolver =
            (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];

        // Ask user which second factor to use.
        // ...

        // Then:
        FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];

        // Send SMS verification code
        [FIRPhoneAuthProvider.provider
          verifyPhoneNumberWithMultiFactorInfo:hint
                                    UIDelegate:nil
                            multiFactorSession:resolver.session
                                    completion:^(NSString * _Nullable verificationID,
                                                NSError * _Nullable error) {
            if (error != nil) {
                // Failed to verify phone number.
            }

            // Ask user for the SMS verification code.
            // ...

            // Then:
            FIRPhoneAuthCredential *credential =
                [FIRPhoneAuthProvider.provider
                  credentialWithVerificationID:verificationID
                              verificationCode:kPhoneSecondFactorVerificationCode];
            FIRMultiFactorAssertion *assertion =
                [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

            // Complete sign-in.
            [resolver resolveSignInWithAssertion:assertion
                                      completion:^(FIRAuthDataResult * _Nullable authResult,
                                                    NSError * _Nullable error) {
                if (error != nil) {
                    // User successfully signed in with the second factor phone number.
                }
            }];
        }];
    }
}];

恭喜!您使用多重身份验证成功登录了用户。

下一步是什么