如果您已升级到 Firebase Authentication with Identity Platform,可以向应用添加基于时间的动态密码 (TOTP) 多重身份验证 (MFA)。
借助 Firebase Authentication with Identity Platform,您可以将 TOTP 用作 MFA 的其他因素。启用此功能后,尝试登录您的应用的用户会看到系统要求提供 TOTP。为了生成 TOTP,用户必须使用可生成有效 TOTP 代码的身份验证器应用,例如 Google 身份验证器。
准备工作
请至少启用一个支持 MFA 的提供方。请注意,除下列情况之外的所有提供方都支持 MFA:
- 电话身份验证
- 匿名身份验证
- 自定义身份验证令牌
- Apple 游戏中心
确保您的应用验证用户电子邮件地址。MFA 要求验证电子邮件地址。这样可以防止恶意操作者使用别人的电子邮件地址注册服务,然后通过添加第二重身份验证阻止实际的电子邮件地址所有者注册。
如果您尚未安装 Firebase Apple SDK,请进行安装。
只有 iOS 上的 Apple SDK v10.12.0 版及更高版本支持 TOTP MFA。
启用 TOTP MFA
如需启用 TOTP 作为第二重身份验证,请使用 Admin SDK 或调用项目配置 REST 端点。
如需使用 Admin SDK,请执行以下操作:
如果您尚未安装 Firebase Admin Node.js SDK,请进行安装。
只有 Firebase Admin Node.js SDK 11.6.0 版及更高版本支持 TOTP MFA。
运行以下命令:
import { getAuth } from 'firebase-admin/auth'; getAuth().projectConfigManager().updateProjectConfig( { multiFactorConfig: { providerConfigs: [{ state: "ENABLED", totpProviderConfig: { adjacentIntervals: NUM_ADJ_INTERVALS } }] } })
替换以下内容:
NUM_ADJ_INTERVALS
:接受 TOTP 的相邻时间范围间隔数(从 0 到 10)。默认值为 5。TOTP 的工作原理是确保两方(证明器和验证器)在同一时间范围内(通常为 30 秒)生成动态密码时,两者会生成相同的密码。但是,为了适应各方和人工响应时间之间的时钟偏移,您可以将 TOTP 服务配置为也接受相邻时间范围的 TOTP。
如需使用 REST API 启用 TOTP MFA,请运行以下代码:
curl -X PATCH "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=mfa" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-H "X-Goog-User-Project: PROJECT_ID" \
-d \
'{
"mfa": {
"providerConfigs": [{
"state": "ENABLED",
"totpProviderConfig": {
"adjacentIntervals": NUM_ADJ_INTERVALS
}
}]
}
}'
请替换以下内容:
PROJECT_ID
:项目 ID。NUM_ADJ_INTERVALS
:时间范围间隔数(从 0 到 10)。默认值为 5。TOTP 的工作原理是确保两方(证明器和验证器)在同一时间范围内(通常为 30 秒)生成动态密码时,两者会生成相同的密码。但是,为了适应各方和人工响应时间之间的时钟偏移,您可以将 TOTP 服务配置为也接受相邻时间范围的 TOTP。
选择注册模式
您可以选择应用是否要求多重身份验证,以及何时和如何注册用户。一些常见模式包括:
在注册过程中注册用户的第二重身份验证。如果应用要求所有用户进行多重身份验证,请使用此方法。
提供可在注册期间跳过第二重身份验证注册的选项。如果您想要建议但不要求在应用中使用多重身份验证,可以使用此方法。
提供从用户的账号或个人资料管理页面(而不是注册界面)添加第二重身份验证的功能。这样可以使注册过程更顺畅,同时仍可为注重安全的用户提供多重身份验证。
如果用户希望访问安全性要求更高的功能,再要求添加第二重身份验证。
在 TOTP MFA 中注册用户
启用 TOTP MFA 作为应用的第二重身份验证后,请实现客户端逻辑以在 TOTP MFA 中注册用户:
重新验证用户身份。
为经过身份验证的用户生成 TOTP 密文:
// Generate a TOTP secret. guard let mfaSession = try? await currentUser.multiFactor.session() else { return } guard let totpSecret = try? await TOTPMultiFactorGenerator.generateSecret(with: mfaSession) else { return } // Display the secret to the user and prompt them to enter it into their // authenticator app. (See the next step.)
向用户显示密文并提示他们将其输入到身份验证器应用中:
// Display this key: let secret = totpSecret.sharedSecretKey()
除了显示密钥之外,您还可以尝试将其自动添加到设备的默认身份验证器应用中。为此,请生成与 Google 身份验证器兼容的密钥 URI,并将其传递给
openInOTPApp(withQRCodeURL:)
:let otpAuthUri = totpSecret.generateQRCodeURL( withAccountName: currentUser.email ?? "default account", issuer: "Your App Name") totpSecret.openInOTPApp(withQRCodeURL: otpAuthUri)
用户将其密文添加到身份验证器应用中后,应用将开始生成 TOTP。
提示用户输入身份验证器应用显示的 TOTP,并使用它来完成 MFA 注册:
// Ask the user for a verification code from the authenticator app. let verificationCode = // Code from user input. // Finalize the enrollment. let multiFactorAssertion = TOTPMultiFactorGenerator.assertionForEnrollment( with: totpSecret, oneTimePassword: verificationCode) do { try await currentUser.multiFactor.enroll( with: multiFactorAssertion, displayName: "TOTP") } catch { // Wrong or expired OTP. Re-prompt the user. }
让用户通过第二重身份验证登录
如需让用户通过 TOTP MFA 登录,请使用以下代码:
像未使用 MFA 时一样调用一种
signIn(with...:)
方法(例如signIn(withEmail:password:)
)。如果该方法抛出错误且代码为secondFactorRequired
,请启动应用的 MFA 流程。do { let authResult = try await Auth.auth().signIn(withEmail: email, password: password) // If the user is not enrolled with a second factor and provided valid // credentials, sign-in succeeds. // (If your app requires MFA, this could be considered an error // condition, which you would resolve by forcing the user to enroll a // second factor.) // ... } catch let error as AuthErrorCode where error.code == .secondFactorRequired { // Initiate your second factor sign-in flow. (See next step.) // ... } catch { // Other auth error. throw error }
应用的 MFA 流程应首先提示用户选择想要使用的第二重身份验证。您可以通过检查
MultiFactorResolver
实例的hints
属性来获取受支持的第二重身份验证列表:let mfaKey = AuthErrorUserInfoMultiFactorResolverKey guard let resolver = error.userInfo[mfaKey] as? MultiFactorResolver else { return } let enrolledFactors = resolver.hints.map(\.displayName)
如果用户选择使用 TOTP,请提示他们输入身份验证器应用中显示的 TOTP,然后使用该 TOTP 登录:
let multiFactorInfo = resolver.hints[selectedIndex] switch multiFactorInfo.factorID { case TOTPMultiFactorID: let otpFromAuthenticator = // OTP typed by the user. let assertion = TOTPMultiFactorGenerator.assertionForSignIn( withEnrollmentID: multiFactorInfo.uid, oneTimePassword: otpFromAuthenticator) do { let authResult = try await resolver.resolveSignIn(with: assertion) } catch { // Wrong or expired OTP. Re-prompt the user. } default: return }
取消注册 TOTP MFA
本部分介绍如何处理用户取消注册 TOTP MFA 的情况。
如果用户注册了多个 MFA 选项,并且用户取消注册了最近启用的选项,则会接收到 auth/user-token-expired
并退出账号。用户必须重新登录并验证其现有凭据,例如电子邮件地址和密码。
如需取消注册用户、处理错误并触发重新身份验证,请使用以下代码:
guard let currentUser = Auth.auth().currentUser else { return }
// Prompt the user to select a factor to unenroll, from this array:
currentUser.multiFactor.enrolledFactors
// ...
// Unenroll the second factor.
let multiFactorInfo = currentUser.multiFactor.enrolledFactors[selectedIndex]
do {
try await currentUser.multiFactor.unenroll(with: multiFactorInfo)
} catch let error as AuthErrorCode where error.code == .invalidUserToken {
// Second factor unenrolled, but the user was signed out. Re-authenticate
// them.
}
后续步骤
- 使用 Admin SDK 以编程方式管理多重身份验证用户。