
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
@otplib/core
Advanced tools
Core types, interfaces, and utilities for the otplib OTP library suite.
@otplib/core provides the foundational abstractions for all otplib packages:
CryptoContextBase32ContextThis package is primarily used as a dependency by other otplib packages. Direct usage is only necessary when building custom plugins or extending the library.
[!IMPORTANT] Breaking Changes (v13) The
totpandhotpspecific logic have been moved to their individual packages.See Getting Started for details.
npm install @otplib/core
pnpm add @otplib/core
yarn add @otplib/core
@otplib/core provides baseline functionality and definitions for the library suite. It defines the errors, input validations and the plugin interfaces.
All otplib errors extend OTPError. Import concrete subclasses to distinguish them in a catch block.
import { Base32DecodeError, HMACError, SecretTooShortError } from "@otplib/core";
try {
const token = await generate({ secret, crypto, base32 });
} catch (err) {
if (err instanceof SecretTooShortError) {
// secret is fewer than 16 bytes (128 bits)
} else if (err instanceof Base32DecodeError) {
// secret string contained invalid Base32 characters
} else if (err instanceof HMACError) {
// the crypto plugin's HMAC operation failed; err.cause holds the original error
}
}
Use the validation utilities before passing values to generation or verification functions.
import { validateSecret, validateToken, validateCounter } from "@otplib/core";
// validateSecret accepts a decoded Uint8Array
validateSecret(decodedSecretBytes); // throws SecretTooShortError / SecretTooLongError
// validateToken checks length and digit-only format
validateToken(token, 6); // throws TokenLengthError or TokenFormatError
// validateCounter checks for negatives, non-integers, and overflow
validateCounter(counter); // throws CounterNegativeError / CounterNotIntegerError / CounterOverflowError
createCryptoPlugin wraps your HMAC and random-bytes implementations into a CryptoPlugin that any otplib package accepts.
import { createCryptoPlugin } from "@otplib/core";
const myCrypto = createCryptoPlugin({
name: "my-crypto",
hmac: async (algorithm, key, data) => {
// Return Uint8Array — async and sync returns are both accepted
},
randomBytes: (length) => {
// Return a cryptographically secure Uint8Array of the requested length
},
});
createBase32Plugin wraps encode/decode functions into a Base32Plugin. Use this to bypass Base32 entirely or to integrate an alternative encoding library.
import { createBase32Plugin, stringToBytes, bytesToString } from "@otplib/core";
// Example: UTF-8 passthrough (no Base32 encoding)
const plaintextPlugin = createBase32Plugin({
name: "plaintext",
encode: bytesToString,
decode: stringToBytes,
});
stringToBytes and bytesToString convert between UTF-8 strings and Uint8Array. Use stringToBytes when you have a raw passphrase rather than a Base32-encoded secret.
import { stringToBytes, bytesToString } from "@otplib/core";
// Raw passphrase → Uint8Array for use as secret bytes
const secretBytes = stringToBytes("my-raw-passphrase");
// Uint8Array → string (UTF-8 decode)
const str = bytesToString(secretBytes);
normalizeSecret handles the common case of accepting either a Base32 string or a Uint8Array and returning bytes, given a Base32 plugin.
import { normalizeSecret } from "@otplib/core";
const bytes = normalizeSecret("JBSWY3DPEHPK3PXP", base32Plugin);
// or pass Uint8Array directly — returned unchanged
const bytes2 = normalizeSecret(existingUint8Array);
When using @otplib/core directly (rather than the main otplib bundle), you must supply the crypto and Base32 plugins explicitly.
import { generateSecret } from "@otplib/core";
import { NodeCryptoPlugin } from "@otplib/plugin-crypto-node";
import { ScureBase32Plugin } from "@otplib/plugin-base32-scure";
const secret = generateSecret({
crypto: new NodeCryptoPlugin(),
base32: new ScureBase32Plugin(),
length: 20, // 160 bits — RFC 4226 recommendation
});
Full API reference and usage guides at otplib.yeojz.dev:
@otplib/hotp - HOTP implementation (RFC 4226)@otplib/totp - TOTP implementation (RFC 6238)@otplib/plugin-crypto-node - Node.js crypto plugin@otplib/plugin-crypto-web - Web Crypto API plugin@otplib/plugin-crypto-noble - Noble hashes crypto plugin@otplib/plugin-base32-scure - Base32 plugin using @scure/baseSpeakeasy is a library for generating and verifying one-time passwords (OTPs) using TOTP and HOTP algorithms. It offers similar functionalities to @otplib/core but also includes additional features like QR code generation for easier secret sharing.
Notp is a minimalistic library for generating and verifying TOTP and HOTP tokens. It is lightweight and easy to use, making it a good alternative to @otplib/core for simpler use cases.
OtpAuth is a library that provides a comprehensive set of tools for generating and validating OTPs, including support for TOTP and HOTP. It also offers additional features like URI generation for easy integration with OTP apps.
FAQs
Core interfaces, types, and crypto abstraction for otplib
The npm package @otplib/core receives a total of 2,442,400 weekly downloads. As such, @otplib/core popularity was classified as popular.
We found that @otplib/core 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.