如果您已升级到 Firebase Authentication with Identity Platform,可以向应用添加基于时间的动态密码 (TOTP) 多重身份验证 (MFA)。
借助 Firebase Authentication with Identity Platform,您可以将 TOTP 用作 MFA 的其他因素。启用此功能后,尝试登录您的应用的用户会看到系统要求提供 TOTP。为了生成 TOTP,用户必须使用可生成有效 TOTP 代码的身份验证器应用,例如 Google 身份验证器。
准备工作
请至少启用一个支持 MFA 的提供方。请注意,除下列情况之外的所有提供方都支持 MFA:
- 电话身份验证
- 匿名身份验证
- 自定义身份验证令牌
- Apple 游戏中心
确保您的应用验证用户电子邮件地址。MFA 要求验证电子邮件地址。这样可以防止恶意操作者使用别人的电子邮件地址注册服务,然后通过添加第二重身份验证阻止实际的电子邮件地址所有者注册。
如果您尚未安装 Firebase JavaScript SDK,请进行安装。
只有模块化 Web SDK v9.19.1 版及更高版本支持 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 中注册用户:
导入所需的 MFA 类和函数:
import { multiFactor, TotpMultiFactorGenerator, TotpSecret, getAuth, } from "firebase/auth";
重新验证用户身份。
为经过身份验证的用户生成 TOTP 密文:
// Generate a TOTP secret. const multiFactorSession = await multiFactor(currentUser).getSession(); const totpSecret = await TotpMultiFactorGenerator.generateSecret( multiFactorSession );
向用户显示密文并提示他们将其输入到身份验证器应用中。
借助许多身份验证器应用,用户可以通过扫描代表与 Google 身份验证器兼容的密钥 URI 的二维码,快速添加新的 TOTP 密钥。如需为此目的生成二维码,请使用
generateQrCodeUrl()
生成 URI,然后使用您选择的二维码库对其进行编码。例如:const totpUri = totpSecret.generateQrCodeUrl( currentUser.email, "Your App's Name" ); await QRExampleLib.toCanvas(totpUri, qrElement);
无论您是否显示二维码,都应始终显示密钥,以支持无法读取二维码的身份验证器应用:
// Also display this key: const secret = totpSecret.secretKey;
用户将其密文添加到身份验证器应用中后,应用将开始生成 TOTP。
提示用户输入身份验证器应用上显示的 TOTP,并使用它来完成 MFA 注册:
// Ask the user for a verification code from the authenticator app. const verificationCode = // Code from user input. // Finalize the enrollment. const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment( totpSecret, verificationCode ); await multiFactor(currentUser).enroll(multiFactorAssertion, mfaDisplayName);
让用户通过第二重身份验证登录
如需让用户通过 TOTP MFA 登录,请使用以下代码:
导入所需的 MFA 类和函数:
import { getAuth, getMultiFactorResolver, TotpMultiFactorGenerator, } from "firebase/auth";
像未使用 MFA 时一样调用一种
signInWith
方法(例如signInWithEmailAndPassword()
)。如果该方法抛出auth/multi-factor-auth-required
错误,请启动应用的 MFA 流程。try { const userCredential = await signInWithEmailAndPassword( getAuth(), email, 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 (error) { switch (error.code) { case "auth/multi-factor-auth-required": // Initiate your second factor sign-in flow. (See next step.) // ... break; case ...: // Handle other errors, such as wrong passwords. break; } }
应用的 MFA 流程应首先提示用户选择想要使用的第二重身份验证。您可以通过检查
MultiFactorResolver
实例的hints
属性来获取受支持的第二重身份验证列表:const mfaResolver = getMultiFactorResolver(getAuth(), error); const enrolledFactors = mfaResolver.hints.map(info => info.displayName);
如果用户选择使用 TOTP,请提示他们输入身份验证器应用中显示的 TOTP,然后使用该 TOTP 登录:
switch (mfaResolver.hints[selectedIndex].factorId) { case TotpMultiFactorGenerator.FACTOR_ID: const otpFromAuthenticator = // OTP typed by the user. const multiFactorAssertion = TotpMultiFactorGenerator.assertionForSignIn( mfaResolver.hints[selectedIndex].uid, otpFromAuthenticator ); try { const userCredential = await mfaResolver.resolveSignIn( multiFactorAssertion ); // Successfully signed in! } catch (error) { // Invalid or expired OTP. } break; case PhoneMultiFactorGenerator.FACTOR_ID: // Handle SMS second factor. break; default: // Unsupported second factor? break; }
取消注册 TOTP MFA
本部分介绍如何处理用户取消注册 TOTP MFA 的情况。
如果用户注册了多个 MFA 选项,并且用户取消注册了最近启用的选项,则会接收到 auth/user-token-expired
并退出账号。用户必须重新登录并验证其现有凭据,例如电子邮件地址和密码。
如需取消注册用户、处理错误并触发重新身份验证,请使用以下代码:
import {
EmailAuthProvider,
TotpMultiFactorGenerator,
getAuth,
multiFactor,
reauthenticateWithCredential,
} from "firebase/auth";
try {
// Unenroll from TOTP MFA.
await multiFactor(currentUser).unenroll(mfaEnrollmentId);
} catch (error) {
if (error.code === 'auth/user-token-expired') {
// 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.
const credential = EmailAuthProvider.credential(email, password);
await reauthenticateWithCredential(
currentUser,
credential
);
}
}
后续步骤
- 使用 Admin SDK 以编程方式管理多重身份验证用户。