@otplib/totp
RFC 6238 TOTP implementation for otplib.
Installation
npm install @otplib/totp
pnpm install @otplib/totp
yarn add @otplib/totp
Usage
generate
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,
]);
const token = await generate({
secret,
crypto,
algorithm: "sha1",
digits: 6,
period: 30,
epoch: Date.now() / 1000,
});
With Base32 secrets
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,
});
With raw string secrets
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",
base32: bypassAsHex,
crypto,
});
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
Verify a TOTP code:
import { verify } from "@otplib/totp";
import { crypto } from "@otplib/plugin-crypto-node";
const result = await verify({
secret,
token: "123456",
crypto,
algorithm: "sha1",
digits: 6,
period: 30,
epoch: Date.now() / 1000,
epochTolerance: 30,
afterTimeStep: lastTimeStep,
});
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.
getRemainingTime
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();
const seconds2 = getRemainingTime(epoch, 30);
getTimeStepUsed
Get the current TOTP counter (time step) value. All parameters are optional:
import { getTimeStepUsed } from "@otplib/totp";
const counter = getTimeStepUsed();
const counter2 = getTimeStepUsed(epoch, 30);
Sync Variants
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 });
Compatibility with Authenticator Apps
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:
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.
Documentation
Full documentation available at otplib.yeojz.dev:
License
MIT