如果您已升级到 Firebase Authentication with Identity Platform,可以向应用添加基于时间的动态密码 (TOTP) 多重身份验证 (MFA)。
借助 Firebase Authentication with Identity Platform,您可以将 TOTP 用作 MFA 的其他因素。启用此功能后,尝试登录您的应用的用户会看到系统要求提供 TOTP。为了生成 TOTP,用户必须使用可生成有效 TOTP 代码的身份验证器应用,例如 Google 身份验证器。
准备工作
请至少启用一个支持 MFA 的提供方。请注意,除下列情况之外的所有提供方都支持 MFA:
- 电话身份验证
- 匿名身份验证
- 自定义身份验证令牌
- Apple 游戏中心
确保您的应用验证用户电子邮件地址。MFA 要求验证电子邮件地址。这样可以防止恶意操作者使用别人的电子邮件地址注册服务,然后通过添加第二重身份验证阻止实际的电子邮件地址所有者注册。
如果您尚未安装 Firebase Android SDK,请进行安装。
只有 Android SDK v22.1.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. Firebase.auth.currentUser.multiFactor.session .addOnSuccessListener { multiFactorSession -> TotpMultiFactorGenerator.generateSecret(multiFactorSession) .addOnSuccessListener { totpSecret -> // Display the secret to the user and prompt them to // enter it into their authenticator app. (See the next // step.) } }
向用户显示密文并提示他们将其输入到身份验证器应用中:
// Display this key: val secret = totpSecret.sharedSecretKey
除了显示密钥之外,您还可以尝试将其自动添加到设备的默认身份验证器应用中。为此,请生成与 Google 身份验证器兼容的密钥 URI,并将其传递给
openInOtpApp()
:val qrCodeUri = totpSecret.generateQrCodeUrl( currentUser.email ?: "default account", "Your App Name") totpSecret.openInOtpApp(qrCodeUri)
用户将其密文添加到身份验证器应用中后,应用将开始生成 TOTP。
提示用户输入身份验证器应用显示的 TOTP,并使用它来完成 MFA 注册:
// Ask the user for a verification code from the authenticator app. val verificationCode = // Code from user input. // Finalize the enrollment. val multiFactorAssertion = TotpMultiFactorGenerator .getAssertionForEnrollment(totpSecret, verificationCode) Firebase.auth.currentUser.multiFactor.enroll(multiFactorAssertion, "TOTP") .addOnSuccessListener { // Enrollment complete. }
让用户通过第二重身份验证登录
如需让用户通过 TOTP MFA 登录,请使用以下代码:
像未使用 MFA 时一样调用一种
signInWith
方法(例如signInWithEmailAndPassword()
)。如果该方法抛出FirebaseAuthMultiFactorException
,请启动应用的 MFA 流程。Firebase.auth.signInWithEmailAndPassword(email, password) .addOnSuccessListener { result -> // 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.) // ... } .addOnFailureListener { exception -> when (exception) { is FirebaseAuthMultiFactorException -> { // Initiate your second factor sign-in flow. (See next step.) // ... } } }
应用的 MFA 流程应首先提示用户选择想要使用的第二重身份验证。您可以通过检查
MultiFactorResolver
实例的hints
属性来获取受支持的第二重身份验证列表:val enrolledFactors = exception.resolver.hints.map { it.displayName }
如果用户选择使用 TOTP,请提示他们输入身份验证器应用中显示的 TOTP,然后使用该 TOTP 登录:
when (exception.resolver.hints[selectedIndex].factorId) { TotpMultiFactorGenerator.FACTOR_ID -> { val otpFromAuthenticator = // OTP typed by the user. val assertion = TotpMultiFactorGenerator.getAssertionForSignIn( exception.resolver.hints[selectedIndex].uid, otpFromAuthenticator ) exception.resolver.resolveSignIn(assertion) .addOnSuccessListener { result -> // Successfully signed in! } .addOnFailureListener { resolveError -> // Invalid or expired OTP. } } PhoneMultiFactorGenerator.FACTOR_ID -> { // Handle SMS second factor. } }
取消注册 TOTP MFA
本部分介绍如何处理用户取消注册 TOTP MFA 的情况。
如果用户注册了多个 MFA 选项,并且用户取消注册了最近启用的选项,则会接收到 auth/user-token-expired
并退出账号。用户必须重新登录并验证其现有凭据,例如电子邮件地址和密码。
如需取消注册用户、处理错误并触发重新身份验证,请使用以下代码:
Firebase.auth.currentUser.multiFactor.unenroll(mfaEnrollmentId)
.addOnSuccessListener {
// Second factor unenrolled.
}
.addOnFailureListener { exception ->
when (exception) {
is FirebaseAuthInvalidUserException -> {
// Second factor unenrolled. If the user was signed out, re-authenticate
// them.
// For example, if they signed in with a password, prompt them to
// provide it again, then call `reauthenticateWithCredential()` as shown
// below.
val credential = EmailAuthProvider.getCredential(email, password)
currentUser.reauthenticate(credential)
.addOnSuccessListener {
// Success!
}
.addOnFailureListener {
// Bad email address and password combination.
}
}
}
}
后续步骤
- 使用 Admin SDK 以编程方式管理多重身份验证用户。