
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
@otplib/totp
Advanced tools
RFC 6238 TOTP implementation for otplib.
npm install @otplib/totp
pnpm install @otplib/totp
yarn add @otplib/totp
Generate a TOTP code:
import { generate } from "@otplib/totp";
import { crypto } from "@otplib/plugin-crypto-node";
const secret = new Uint8Array([
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x30,
]); // 20-byte HMAC key
const token = await generate({
secret, // Required: Uint8Array or Base32 string
crypto, // Required: crypto plugin
algorithm: "sha1", // Optional: 'sha1' | 'sha256' | 'sha512'
digits: 6, // Optional: 6 | 7 | 8
period: 30, // Optional: time step in seconds
epoch: Date.now() / 1000, // Optional: Unix timestamp in seconds (defaults to now)
});
If your secret is a Base32 string (e.g., from Google Authenticator), provide a base32 plugin to decode it:
import { generate } from "@otplib/totp";
import { crypto } from "@otplib/plugin-crypto-node";
import { base32 } from "@otplib/plugin-base32-scure";
const token = await generate({
secret: "GEZDGNBVGY3TQOJQGEZDGNBVGY",
crypto,
base32, // Required when secret is a string
});
If your secret is a plain string (e.g. a passphrase), convert it to Uint8Array first using stringToBytes from @otplib/core:
import { generate } from "@otplib/totp";
import { crypto } from "@otplib/plugin-crypto-node";
import { stringToBytes } from "@otplib/core";
const token = await generate({
secret: stringToBytes("mysecretpassphrase"),
crypto,
});
Alternatively, @otplib/plugin-base32-alt provides bypass plugins for secrets in other encodings:
import { generate } from "@otplib/totp";
import { crypto } from "@otplib/plugin-crypto-node";
import { bypassAsHex } from "@otplib/plugin-base32-alt";
const token = await generate({
secret: "48656c6c6f", // hex-encoded secret
base32: bypassAsHex,
crypto,
});
| Name | Input format |
|---|---|
bypassAsString | Raw UTF-8 string |
bypassAsHex | Hex string |
bypassAsBase64 | Base64 string |
[!NOTE] Raw string and bypass secrets are not compatible with authenticator apps or
otpauth://URIs, which always expect Base32-encoded secrets.
Verify a TOTP code:
import { verify } from "@otplib/totp";
import { crypto } from "@otplib/plugin-crypto-node";
const result = await verify({
secret, // Required: Uint8Array or Base32 string
token: "123456", // Required: token to verify
crypto, // Required: crypto plugin
algorithm: "sha1", // Optional: hash algorithm
digits: 6, // Optional: expected digits
period: 30, // Optional: time step in seconds
epoch: Date.now() / 1000, // Optional: Unix timestamp in seconds (defaults to now)
epochTolerance: 30, // Optional: number or [past, future] tuple
afterTimeStep: lastTimeStep, // Optional: reject time steps <= this value (replay protection)
});
// Returns: { valid: true, delta: number, epoch: number, timeStep: number } | { valid: false }
epochTolerance accepts a number (symmetric window ±n seconds) or a [past, future] tuple for asymmetric control (e.g., [30, 0] accepts only past tokens, as recommended by RFC 6238 for transmission-delay handling).
afterTimeStep is an exclusive lower bound on the matched time step: any match at timeStep <= afterTimeStep is rejected. Pass the timeStep from the previous successful verification to prevent token reuse.
Get remaining seconds until the next TOTP period. All parameters are optional and default to the current time with a 30-second period:
import { getRemainingTime } from "@otplib/totp";
const seconds = getRemainingTime(); // uses current time, period=30, t0=0
const seconds2 = getRemainingTime(epoch, 30); // explicit time and period
Get the current TOTP counter (time step) value. All parameters are optional:
import { getTimeStepUsed } from "@otplib/totp";
const counter = getTimeStepUsed(); // uses current time, period=30, t0=0
const counter2 = getTimeStepUsed(epoch, 30); // explicit time and period
generateSync and verifySync are synchronous alternatives with the same signatures. They require a crypto plugin that supports sync HMAC operations, such as @otplib/plugin-crypto-node or @otplib/plugin-crypto-noble. Using them with @otplib/plugin-crypto-web will throw.
import { generateSync, verifySync } from "@otplib/totp";
import { crypto } from "@otplib/plugin-crypto-node";
const secret = new Uint8Array([
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x30,
]);
const token = generateSync({ secret, crypto });
const result = verifySync({ secret, token, crypto });
RFC 4226 (HOTP) and RFC 6238 (TOTP) define flexible algorithms that allow different hash functions, digit lengths, and time steps. However, most authenticator apps (Google Authenticator, Authy, Microsoft Authenticator, 1Password, etc.) and services offering 2 factor authentication use the following defaults:
| Parameter | Value |
|---|---|
algorithm | sha1 |
digits | 6 |
period | 30 |
secret | Base32 string |
If you are deviating from these values, do validate that it is supported by the target application.
If you need to provision an authenticator app via QR code, use @otplib/uri to generate an otpauth://totp/ URI.
Full documentation available at otplib.yeojz.dev:
Speakeasy is a popular package for generating and verifying one-time passwords, including both TOTP and HOTP. It offers similar functionality to @otplib/totp, with a focus on simplicity and ease of use. Speakeasy is widely used for implementing 2FA in Node.js applications.
notp is another package that provides support for TOTP and HOTP. It is a lightweight library that offers basic functionality for generating and verifying OTPs. Compared to @otplib/totp, notp is more minimalistic and may lack some of the advanced configuration options available in otplib.
Authenticator is a package designed for generating and verifying TOTP tokens. It is similar to @otplib/totp in terms of functionality but is designed to be a simple and straightforward solution for adding 2FA to applications. It may not offer as many customization options as otplib.
FAQs
RFC 6238 TOTP implementation for otplib
The npm package @otplib/totp receives a total of 867,399 weekly downloads. As such, @otplib/totp popularity was classified as popular.
We found that @otplib/totp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.