2fa-node

中文文档
1.0.0发布于 2026/09/30MPL-2.0TypeScriptES Module近 30 天下载 1926

Easy 2-Factor Integration For Node.js

原始使用文档
文档目录22 节
  1. Features
  2. Installation
  3. Usage
  4. Generate a TOTP secret
  5. Generate an HOTP secret
  6. Verify a TOTP token
  7. Accept each TOTP token only once
  8. Generate a TOTP token
  9. Verify an HOTP token
  10. Generate an HOTP token
  11. API Documentation
  12. generateSecret(options: SecretOptions, type: OtpType = "TOT…
  13. generateToken(secret: string): { token: string } | null
  14. verifyToken(secret: string, token?: string, window: number …
  15. verifyTokenOnce(secret: string, token?: string, lastTimeSte…
  16. generateHOTPToken(secret: string, counter: number = 0): { t…
  17. verifyHOTPToken(secret: string, token: string | undefined, …
  18. Types
  19. Security notes
  20. Upgrading from 0.x
  21. Dependencies
  22. License

2FA NODE

This library generates and verifies time-based one-time passwords (TOTP, RFC 6238) and HMAC-based one-time passwords (HOTP, RFC 4226) for two-factor authentication (2FA). It also creates the secret, the otpauth:// URI and the QR code that authenticator apps such as Google Authenticator or Authy scan.

Features

  • Generate a 2FA secret: a random base32 secret, its otpauth:// URI and a QR code of that URI.
  • Generate and verify TOTP tokens: 6-digit codes that change every 30 seconds, optionally accepting each token only once.
  • Generate and verify HOTP tokens: 6-digit codes tied to a counter.

Installation

npm install 2fa-node

Requires Node.js 20.19 or later.

Usage

Generate a TOTP secret

import { generateSecret } from "2fa-node";

const { secret, uri, qr } = await generateSecret({
  name: "MyApp", // issuer shown in the authenticator app
  account: "[email protected]", // account shown in the authenticator app
});

console.log(secret); // store it for the user
console.log(uri); // otpauth://totp/MyApp:user%40example.com?secret=...&issuer=MyApp
console.log(qr); // PNG data URL to show to the user

Generate an HOTP secret

import { generateSecret } from "2fa-node";

const { secret, uri, qr } = await generateSecret(
  { name: "MyApp", account: "[email protected]", counter: 0 },
  "HOTP",
);

Verify a TOTP token

import { verifyToken } from "2fa-node";

const secret = "JBSWY3DPEHPK3PXP"; // the user's stored secret
const token = "123456"; // the token typed by the user

const isValid = verifyToken(secret, token);

console.log(isValid); // true if valid, false otherwise

Accept each TOTP token only once

import { verifyTokenOnce } from "2fa-node";

const secret = "JBSWY3DPEHPK3PXP"; // the user's stored secret
const token = "123456"; // the token typed by the user
const lastTimeStep = null; // the user's stored time step, null before the first login

const match = verifyTokenOnce(secret, token, lastTimeStep);

if (match) {
  // Save match.timeStep as the user's lastTimeStep, so this token cannot be used again.
}

Generate a TOTP token

import { generateToken } from "2fa-node";

const secret = "JBSWY3DPEHPK3PXP";
const result = generateToken(secret);

console.log(result?.token); // the current token, or undefined if the secret is invalid

Verify an HOTP token

import { verifyHOTPToken } from "2fa-node";

const secret = "JBSWY3DPEHPK3PXP"; // the user's stored secret
const token = "123456"; // the token typed by the user
const counter = 1; // the user's stored counter

const isValid = verifyHOTPToken(secret, token, counter);

if (isValid) {
  // Save counter + 1 so this token cannot be used again.
}

Generate an HOTP token

import { generateHOTPToken } from "2fa-node";

const secret = "JBSWY3DPEHPK3PXP";
const counter = 1;

const result = generateHOTPToken(secret, counter);

console.log(result?.token); // the token, or undefined if the secret is invalid

API Documentation

Tokens have 6 digits and use HMAC-SHA1; TOTP tokens change every 30 seconds. Secrets are case-insensitive base32 strings of at least 80 bits (16 characters).

generateSecret(options: SecretOptions, type: OtpType = "TOTP"): Promise<{ secret: string; uri: string; qr: string }>

Generates a random secret for two-factor authentication.

  • Parameters:
    • options.name: issuer shown in the authenticator app, usually your product name.
    • options.account: account shown in the authenticator app, usually the user's email.
    • options.counter (optional): initial HOTP counter written to the URI. Defaults to 0; ignored for TOTP.
    • options.numberOfSecretBytes (optional): size of the secret in bytes. Defaults to 20; must be an integer of at least 16, because RFC 4226 requires 128-bit secrets.
    • type: "TOTP" (default) or "HOTP".
  • Returns: a Promise resolving to:
    • secret: the base32 secret to store for the user.
    • uri: the otpauth:// URI.
    • qr: a PNG data URL of the URI's QR code.
  • Throws: TypeError for an unknown type; RangeError for an invalid numberOfSecretBytes or HOTP counter.

generateToken(secret: string): { token: string } | null

Generates the current TOTP token.

  • Returns: { token }, or null if the secret is invalid.

verifyToken(secret: string, token?: string, window: number | [number, number] = 1): boolean

Verifies a TOTP token.

  • Parameters:
    • window: how many 30-second steps before and after now are also accepted. Use [past, future] for different values; 0 accepts only the current step.
  • Returns: true if the token is valid; false otherwise, including when the token is missing or malformed or the secret is invalid.
  • Throws: RangeError if window is not made of non-negative integers or spans more than 98 steps in total.

verifyTokenOnce(secret: string, token?: string, lastTimeStep?: number | null, window: number | [number, number] = 1): { timeStep: number } | null

Verifies a TOTP token like verifyToken, but also rejects tokens from lastTimeStep or earlier, so each token is accepted only once.

  • Parameters:
    • lastTimeStep: the timeStep returned by the user's last successful check, or null/undefined if there is none.
    • window: same as in verifyToken.
  • Returns: { timeStep } with the 30-second step the token belongs to, or null if the token is rejected. Save timeStep for the user after each success.
  • Throws: the same errors as verifyToken, plus a RangeError if lastTimeStep is not a non-negative safe integer.

generateHOTPToken(secret: string, counter: number = 0): { token: string } | null

Generates the HOTP token for a counter.

  • Returns: { token }, or null if the secret is invalid.
  • Throws: RangeError if counter is not a non-negative safe integer.

verifyHOTPToken(secret: string, token: string | undefined, counter: number): boolean

Verifies an HOTP token against exactly the given counter.

  • Returns: true if the token is valid; false otherwise, including when the token is missing or malformed or the secret is invalid.
  • Throws: RangeError if counter is not a non-negative safe integer.

Types

SecretOptions and OtpType ("TOTP" | "HOTP") are exported for TypeScript users.

Security notes

  • verifyToken accepts the same token again while it is inside the window. To block replays, use verifyTokenOnce, including for the check that confirms enrollment, and save the returned timeStep only if it moves forward. Do it in a single statement and treat zero updated rows as a failed login, so two simultaneous requests with the same token cannot both succeed:

    UPDATE users SET last_time_step = $step
    WHERE id = $id AND (last_time_step IS NULL OR last_time_step < $step);
    

    For HOTP, advance the counter the same way: UPDATE users SET counter = counter + 1 WHERE id = $id AND counter = $counter.

  • Limit how many verification attempts a user can make.

  • Keep secrets encrypted at rest.

Upgrading from 0.x

  • HOTP tokens now follow RFC 4226 and match authenticator apps, so they differ from the tokens 0.x produced.
  • verifyToken and verifyHOTPToken return false instead of null when the token is missing.
  • verifyToken accepts one step (30 seconds) before and after now by default, instead of four.
  • verifyHOTPToken requires the counter and no longer takes a window argument, which never had any effect.
  • Secrets must be canonical base32 of at least 80 bits. Secrets that 0.x accepted, such as ones with spaces, extra = padding or non-zero unused trailing bits, now make the generate functions return null and the verify functions return false.
  • numberOfSecretBytes must be at least 16. Invalid types, counters and windows throw.
  • The URI carries the standard issuer parameter instead of name.
  • Only the package entry point can be imported: deep imports such as 2fa-node/dist/secret.js no longer work.
  • Node.js 20.19 or later is required.
  • The license changed from MIT to MPL-2.0.

Dependencies

  • otplib - A library for generating and verifying one-time passwords.
  • qrcode - A library for generating QR codes.

License

This project is licensed under the Mozilla Public License 2.0 - see the LICENSE.md file for details.

版本与发布记录

2fa-node 共发布 7 个版本,首次发布于 2025/01/11;过去 12 个月发布了 1 个版本。

版本发布日期距今
1.0.0latest2026/09/301 个月前
0.0.62025/01/221.8 年前
0.0.52025/01/171.8 年前
0.0.42025/01/121.8 年前
0.0.32025/01/111.8 年前
0.0.22025/01/111.8 年前

关于 2fa-node 的常见问题

2fa-node 是什么?

Easy 2-Factor Integration For Node.js

如何安装 2fa-node?

在项目目录运行 npm install 2fa-node,或使用 pnpm add / yarn add / bun add。

2fa-node 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

2fa-node 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

2fa-node 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.19.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

2fa-node 可以商用吗?

2fa-node 使用 MPL-2.0 许可。弱 Copyleft:可以商用,也可以和闭源代码一起使用;但如果分发时修改了 MPL 授权的文件,这些文件的修改需以 MPL 开源。以上为概括,不构成法律意见,请以许可原文为准。

2fa-node 的下载量有多少?

根据 npm 官方下载统计,2fa-node 在 2026/10/04 – 2026/11/02 这 30 天内被下载 1,926 次,日均约 64 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

2fa-node 还在维护吗?

最新版本 1.0.0 发布于 2026/09/30(1 个月前);至今共发布 7 个版本,其中过去 12 个月发布了 1 个;首次发布于 2025/01/11。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。