
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
@otplib/hotp
Advanced tools
RFC 4226 HOTP implementation for otplib.
npm install @otplib/hotp
pnpm install @otplib/hotp
yarn add @otplib/hotp
Generate an HOTP code for a specific counter:
import { generate } from "@otplib/hotp";
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
counter: 0, // Required: counter value
crypto, // Required: crypto plugin
algorithm: "sha1", // Optional: 'sha1' | 'sha256' | 'sha512'
digits: 6, // Optional: 6 | 7 | 8
});
If your secret is a Base32 string (e.g., from Google Authenticator), provide a base32 plugin to decode it:
import { generate } from "@otplib/hotp";
import { crypto } from "@otplib/plugin-crypto-node";
import { base32 } from "@otplib/plugin-base32-scure";
const token = await generate({
secret: "GEZDGNBVGY3TQOJQGEZDGNBVGY",
counter: 0,
crypto,
base32, // Required when secret is a string
});
Verify an HOTP code:
import { verify } from "@otplib/hotp";
import { crypto } from "@otplib/plugin-crypto-node";
const result = await verify({
secret, // Required: Uint8Array or Base32 string
token: "123456", // Required: token to verify
counter: 0, // Required: expected counter
crypto, // Required: crypto plugin
algorithm: "sha1", // Optional: hash algorithm
digits: 6, // Optional: expected digits
counterTolerance: 5, // Optional: number or [past, future] tuple
});
// Returns: { valid: true, delta: number } | { valid: false }
counterTolerance accepts a plain number (creates a look-ahead-only window [0, n], the secure default per RFC 4226) or a [past, future] tuple for explicit control (e.g., [2, 5]).
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/hotp";
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, counter: 0, crypto });
const result = verifySync({ secret, token, counter: 0, crypto });
Full documentation available at otplib.yeojz.dev:
Speakeasy is a popular library for generating and verifying one-time passwords, including both HOTP and TOTP. It offers similar functionality to @otplib/hotp but also includes additional features like QR code generation for easier integration with mobile authenticator apps.
notp is a simple library for generating and verifying both HOTP and TOTP tokens. It is lightweight and easy to use, similar to @otplib/hotp, but may not have as many configuration options or additional features as otplib.
FAQs
RFC 4226 HOTP implementation for otplib
The npm package @otplib/hotp receives a total of 741,599 weekly downloads. As such, @otplib/hotp popularity was classified as popular.
We found that @otplib/hotp 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.