otplib API Documentation / @otplib/core
@otplib/core
Core types, interfaces, and utilities for the otplib OTP library suite.
Overview
@otplib/core provides the foundational abstractions for all otplib packages:
- Type Definitions - TypeScript interfaces for OTP operations
- Error Classes - Hierarchical error types for validation and crypto operations
- Validation Utilities - Input validation for secrets, counters, time, and tokens
- Crypto Abstraction - Pluggable crypto backend via
CryptoContext - Base32 Abstraction - Pluggable Base32 encoding/decoding via
Base32Context
This package is primarily used as a dependency by other otplib packages. Direct usage is only necessary when building custom plugins or extending the library.
Breaking Changes (v13)
The totp and hotp specific logic have been moved to their individual packages.
See Getting Started for details.
Installation
npm install @otplib/core
pnpm add @otplib/core
yarn add @otplib/coreUsage
@otplib/core provides baseline functionality and definitions for the library suite. It defines the errors, input validations and the plugin interfaces.
Catching specific error types
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
}
}Validating inputs
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 / CounterOverflowErrorCreating a custom crypto plugin
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
},
});Creating a custom Base32 plugin
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,
});Converting secret formats
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);Generating a secret with explicit plugins
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
});Documentation
Full API reference and usage guides at otplib.yeojz.dev:
Related Packages
@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/base