accounts-2fa
此包允许您为用户提供一种在其账户上启用 2FA 的方法,可以使用身份验证器应用程序(例如 Google Authenticator 或 1Password)。当用户在您的应用程序上登录时,他们将能够生成一个新的二维码并在他们首选的应用程序上读取此代码。之后,他们将开始接收他们的代码。然后,他们可以在您的应用程序上完成启用 2FA,并且每次他们尝试登录到您的应用程序时,您都可以将他们重定向到一个可以提供他们从身份验证器收到的代码的地方。
为了提供与所有其他实现 TOTP 的身份验证器应用程序和服务完全兼容的代码,此包使用 node-2fa,它基于 notp 工作,**该**包实现了 TOTP (RFC 6238)(身份验证器标准),它基于 HOTP (RFC 4226)。
此包旨在与
accounts-password
或accounts-passwordless
一起使用,因此,如果您在项目中没有这两个包中的任何一个,则需要添加其中一个。将来,我们希望能够将此包与其他登录方法(我们的 oauth 方法(Google、GitHub 等...))一起使用。
2FA 激活流程
为了启用 2FA,第一步是生成一个二维码,以便用户可以在身份验证器应用程序中扫描它并开始接收代码。
Accounts.generate2faActivationQrCode仅限客户端
仅限客户端
摘要
生成 SVG 二维码并在用户处保存密钥
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
appName | 字符串 | 这是您的应用程序的名称,将在用户扫描二维码时显示。 | 是 |
callback | 函数 | 在失败时使用单个 | 是 |
import { Accounts } from "meteor/accounts-base";
Accounts.generate2faActivationQrCode(
"appName",
() => {},
);
接收一个 appName
,它是您的应用程序的名称,将在用户扫描二维码时显示。此外,在成功时使用 SVG 格式的二维码、二维码密钥和可用于在身份验证器应用程序中激活 2FA 的 URI 调用回调,或在失败时使用单个 Error
参数调用回调。
在成功时,此函数还会将一个对象添加到已登录用户的 services 对象中,其中包含二维码密钥
services: {
...
twoFactorAuthentication: {
secret: "***"
}
}
这是一个关于如何调用此函数的示例
import { Buffer } from "buffer";
import { Accounts } from 'meteor/accounts-base';
// component
const [qrCode, setQrCode] = useState(null);
<button
onClick={() => {
Accounts.generate2faActivationQrCode("My app name", (err, result) => {
if (err) {console.error("...", err);return;}
const { svg, secret, uri } = result;
/*
the svg can be converted to base64, then be used like:
<img
width="200"
src={`data:image/svg+xml;base64,${qrCode}`}
/>
*/
setQrCode(Buffer.from(svg).toString('base64'));
})
}}
>
Generate a new code
</button>
此方法可能会失败并抛出以下错误
- “2FA 已激活。您需要先禁用 2FA,然后才能尝试生成新的激活码 [2fa-activated]”如果尝试在用户已启用 2FA 时生成激活。
此时,2FA 尚未激活。现在用户可以访问其身份验证器应用程序生成的代码,您可以调用函数 Accounts.enableUser2fa
Accounts.enableUser2fa仅限客户端
仅限客户端
摘要
启用用户 2FA
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
code | 字符串 | 从身份验证器应用程序接收的代码。 | 是 |
callback | 函数 | 可选回调。在成功时不带参数调用,或在失败时使用单个 | 否 |
import { Accounts } from "meteor/accounts-base";
Accounts.enableUser2fa(
"code",
() => {}, // this param is optional
);
它应该使用用户在读取二维码后从身份验证器应用程序接收的代码调用。回调在失败时使用单个 Error
参数调用。如果提供的代码正确,则 type
将添加到用户的 twoFactorAuthentication
对象中,现在 2FA 被认为已启用
services: {
...
twoFactorAuthentication: {
type: "otp",
secret: "***",
}
}
要验证用户是否启用了 2FA,您可以调用函数 Accounts.has2faEnabled
Accounts.has2faEnabled仅限客户端
仅限客户端
摘要
验证已登录的用户是否启用了 2FA
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
callback | 函数 | 在成功时使用布尔值调用,指示用户是否启用了 2FA,或在失败时使用单个 | 否 |
import { Accounts } from "meteor/accounts-base";
Accounts.has2faEnabled(
() => {}
);
此函数必须在用户登录时调用。
禁用 2FA
要为用户禁用 2FA,请使用此方法
Accounts.disableUser2fa仅限客户端
仅限客户端
摘要
禁用用户 2FA
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
callback | 函数 | 可选回调。在成功时不带参数调用,或在失败时使用单个 | 否 |
import { Accounts } from "meteor/accounts-base";
Accounts.disableUser2fa(
() => {}
);
要调用此函数,用户必须已登录。
使用 2FA 登录
现在您有了一种允许用户在其账户上启用 2FA 的方法,您可以基于此创建登录流程。
如本指南开头所述,此包目前正在与另外两个包一起使用:accounts-password
和 accounts-passwordless
。下面解释了如何将此包与它们一起使用。
与 accounts-password 结合使用
当调用函数 Meteor.loginWithPassword
时,如果为用户启用了 2FA,则会向回调返回错误,以便您可以将用户重定向到可以提供代码的位置。
例如
<button
onClick={() => {
Meteor.loginWithPassword(username, password, (error) => {
if (error) {
if (error.error === "no-2fa-code") {
// send user to a page or show a component
// where they can provide a 2FA code
setShouldAskCode(true);
return;
}
console.error("Error trying to log in (user without 2fa)", error);
}
});
}}
>
Login
</button>
如果未启用 2FA,则用户将正常登录。
您现在需要调用的函数以允许用户登录是 Meteor.loginWithPasswordAnd2faCode
Meteor.loginWithPasswordAnd2faCode仅限客户端
仅限客户端
摘要
使用密码和令牌登录用户。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
selector | 对象或字符串 | 可以是解释为用户名或电子邮件的字符串;或者是一个具有单个键的对象: | 是 |
password | 字符串 | 用户的密码。 | 是 |
token | 字符串 | 用户身份验证器应用程序提供的令牌。 | 是 |
callback | 函数 | 可选回调。在成功时不带参数调用,或在失败时使用单个 | 否 |
import { Meteor } from "meteor/meteor";
Meteor.loginWithPasswordAnd2faCode(
selector,
"password",
"token",
() => {}, // this param is optional
);
现在您将能够从用户那里接收代码,此函数将验证代码是否有效。如果是,则用户将登录。
因此,此函数的调用应该如下所示
<button
onClick={() => {
Meteor.loginWithPasswordAnd2faCode(username, password, code, (error) => {
if (error) {
console.error("Error trying to log in (user with 2fa)", error);
}
});
}}
>
Validate and log in
</button>
此方法可能会失败并抛出以下错误之一
- “必须提供 2FA 代码 [no-2fa-code]”如果没有提供 2FA 代码。
- “无效的 2FA 代码 [invalid-2fa-code]”如果提供的 2FA 代码无效。
与 accounts-passwordless 结合使用
遵循先前包的相同逻辑,如果启用了 2FA,则会向函数 Meteor.passwordlessLoginWithToken
的回调返回错误,然后您可以将用户重定向到可以提供代码的位置。
这是一个示例
<button
onClick={() => {
// logging in just with token
Meteor.passwordlessLoginWithToken(email, token, (error) => {
if (error) {
if (error.error === "no-2fa-code") {
// send user to a page or show a component
// where they can provide a 2FA code
setShouldAskCode(true);
return;
}
console.error("Error verifying token", error);
}
});
}}
>
Validate token
</button>
现在您可以调用函数 Meteor.passwordlessLoginWithTokenAnd2faCode
,它将允许您提供选择器、令牌和 2FA 代码
Meteor.passwordlessLoginWithTokenAnd2faCode仅限客户端
仅限客户端
摘要
使用一次性令牌登录用户。
参数
源代码名称 | 类型 | 描述 | 必需 |
---|---|---|---|
selector | 对象或字符串 | 用户名、电子邮件或自定义选择器以识别用户。 | 是 |
token | 字符串 | 服务器生成的一次性令牌 | 是 |
code | 字符串 | 由用户的身份验证器应用程序生成 | 是 |
callback | 函数 | 可选回调。在成功时不带参数调用,或在失败时使用单个 | 否 |
import { Meteor } from "meteor/meteor";
Meteor.passwordlessLoginWithTokenAnd2faCode(
selector,
"token",
"code",
() => {}, // this param is optional
);
此方法可能会失败并抛出以下错误之一
- “必须提供 2FA 代码 [no-2fa-code]”如果没有提供 2FA 代码。
- “无效的 2FA 代码 [invalid-2fa-code]”如果提供的 2FA 代码无效。
将身份验证包与 accounts-2fa 集成
要将此包与任何其他现有的登录方法集成,需要执行以下两个步骤
1 - 对于客户端,从您当前的登录方法创建一个新方法。例如,从方法 Meteor.loginWithPassword
中,我们创建了一个名为 Meteor.loginWithPasswordAnd2faCode
的新方法,它们之间的唯一区别是后者接收一个额外的参数,即 2FA 代码,但在服务器端我们调用相同的函数。
2 - 对于服务器,在用于登录用户的函数内部,您需要验证函数Accounts._check2faEnabled
是否存在,如果存在,则调用它并提供您想要检查其是否启用了 2FA 的用户对象。如果这两个语句中的任何一个为假,则继续执行登录流程。此函数仅在将包accounts-2fa
添加到项目中时才存在。
如果这两个语句都为真,并且登录验证成功,则您需要验证是否提供了验证码:如果没有提供,则抛出错误;如果提供了,则通过调用函数Accounts._isTokenValid
验证验证码是否有效。如果Accounts._isTokenValid
返回 false,则抛出错误。
以下是一个示例
const result = validateLogin();
if (!result.error && Accounts._check2faEnabled?.(user)) {
if (!code) {
Accounts._handleError("2FA code must be informed.");
}
if (
!Accounts._isTokenValid(user.services.twoFactorAuthentication.secret, code)
) {
Accounts._handleError("Invalid 2FA code.");
}
}
return result;