Skip to content

Migration (v12 Adapter)

This adapter mimics the v12 synchronous API while using v13's plugins under the hood. However, some fundamental changes from v13 may carry over.

Recommendation

This adapter is intended as a temporary bridge. You are still recommended to do a full migration to use v13 directly for full compatibility and future-proofing.

Class/Instance API Only

This adapter only exports the authenticator, totp, and hotp singleton instances and their classes. If you were importing specific utility functions directly from otplib/core or other internal paths in v12, those are not covered by this adapter.

Overview of Changes

v13 introduces a complete rewrite with the following major changes:

  1. Plugin-based architecture - Crypto and Base32 implementations are now pluggable
  2. Async-first API - All token generation and verification is now async
  3. TypeScript-first - Full type safety with comprehensive type definitions
  4. Modular packages - Use only what you need with scoped packages
  5. Functional-First API - Functional API is now recommended over Class API for tree-shaking and simplicity

Quick Migration

Using the v12 Adapter (Drop-in Replacement)

If you want to upgrade to v13 internals but keep your existing v12 code working without changes, use the @otplib/v12-adapter package.

bash
npm install @otplib/v12-adapter

Then update your imports:

typescript
// v12
import { authenticator } from "otplib";

// v13 with Adapter
import { authenticator } from "@otplib/v12-adapter";

Full Migration

Using the Main Package

If you want minimal changes, use the otplib package which includes default plugins:

typescript
// v12
import { authenticator } from "otplib";

const secret = authenticator.generateSecret();
const token = authenticator.generate(secret);
const isValid = authenticator.verify({ token, secret });
typescript
// v13 - Using the Functional API (Recommended)
import { generateSecret, generate, verify } from "otplib";

const secret = generateSecret();
const token = await generate({ secret });
const result = await verify({ secret, token });
const isValid = result.valid;

// v13 - Using the OTP class
import { OTP } from "otplib";

const otp = new OTP();
const secret = otp.generateSecret();
const token = await otp.generate({ secret });
const result = await otp.verify({ secret, token });
const isValid = result.valid;

Using Individual Packages

typescript
// v13 with explicit plugins
import { generate, verify } from "@otplib/totp";
import { crypto } from "@otplib/plugin-crypto-noble";
import { base32 } from "@otplib/plugin-base32-scure";

const token = await generate({
  secret: "GEZDGNBVGY3TQOJQGEZDGNBVGY",
  crypto,
  base32,
});

const result = await verify({
  secret: "GEZDGNBVGY3TQOJQGEZDGNBVGY",
  token,
  crypto,
  base32,
});

Breaking Changes

1. Async API

All generate() and verify() functions are now async by default. You may use the sync variants if you need synchronous behavior.

typescript
// v12 (synchronous)
const token = authenticator.generate(secret);

// v13 (async)
const token = await generate({ secret });

// v13 (sync)
const token = generateSync({ secret });

2. Plugin Injection Required

v13 requires explicit crypto and base32 plugins (except when using the otplib package):

typescript
// v13 - must provide plugins
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,
});

3. Options Object Pattern

Functions now take a single options object instead of positional arguments:

typescript
// v12
authenticator.generate(secret);
authenticator.verify({ token, secret });

// v13
generate({ secret, crypto, base32 });
verify({ secret, token, crypto, base32 });

4. Verify Returns Object

verify() now returns a result object instead of a boolean:

typescript
// v12
const isValid = authenticator.verify({ token, secret }); // boolean

// v13
const result = await verify({ secret, token }); // { valid: boolean; delta?: number; epoch?: number }
const isValid = result.valid;
const delta = result.delta; // 0 = exact match, +/- for window offset
const epoch = result.epoch; // Exact epoch (in seconds) of the period that matched

5. Secret Format Changes

v13 accepts both Base32 strings and raw Uint8Array:

typescript
// Base32 string (requires base32 plugin)
await generate({
  secret: 'GEZDGNBVGY3TQOJQGEZDGNBVGY',
  crypto,
  base32, // required for string secrets
});

// Raw bytes (no base32 plugin needed)
await generate({
  secret: new Uint8Array([...]),
  crypto,
});

6. Secret Length Guardrails

v13 enforces a minimum secret length of 16 bytes. Older deployments may have shorter secrets.

Legacy Secrets

If your existing secrets are shorter than 16 bytes, you may need to override guardrails for backward compatibility. This is not done by default to preserve security. See Danger Zone - Guardrails for details.

6. Class-Based API Changes

The class API has been updated:

typescript
// v12
import { authenticator } from "otplib";
authenticator.options = { digits: 8 };
const token = authenticator.generate(secret);

// v13 - Using the unified OTP class (new)
import { OTP } from "otplib";

const otp = new OTP({
  strategy: "totp", // 'totp' or 'hotp'
  digits: 8,
});

const token = await otp.generate({ secret: "GEZDGNBVGY3TQOJQGEZDGNBVGY" });

// v13 - Using strategy-specific classes
import { TOTP } from "otplib";
import { crypto } from "@otplib/plugin-crypto-noble";
import { base32 } from "@otplib/plugin-base32-scure";

const totp = new TOTP({
  crypto,
  base32,
  digits: 8,
});

totp.secret = "GEZDGNBVGY3TQOJQGEZDGNBVGY";
const token = await totp.generate();

7. Unified Strategy Support

v13.1 introduces a unified API that supports multiple strategies through a single interface:

typescript
import { OTP, generate, verify } from "otplib";

// Using the OTP class with different strategies
const totp = new OTP({ strategy: "totp" });
const hotp = new OTP({ strategy: "hotp" });

// Using functional API with strategy parameter
const token1 = await generate({ secret, strategy: "totp" });
const token2 = await generate({ secret, strategy: "hotp", counter: 0 });

Package Mapping

v12 Packagev13 Package(s)
otplibotplib (all-in-one) or individual packages
otplib/authenticator@otplib/totp
otplib/hotp@otplib/hotp
otplib/totp@otplib/totp
(built-in)@otplib/plugin-crypto-node
(built-in)@otplib/plugin-crypto-noble
(built-in)@otplib/plugin-crypto-web
(built-in)@otplib/plugin-base32-scure
@otplib/core@otplib/core

Choosing Crypto Plugins

v13 offers three crypto plugins:

NodeCryptoPlugin (Node.js only)

typescript
import { crypto } from "@otplib/plugin-crypto-node";
  • Uses Node.js built-in crypto module
  • Synchronous HMAC operations
  • Best performance for Node.js applications

NobleCryptoPlugin (Universal)

typescript
import { crypto } from "@otplib/plugin-crypto-noble";
  • Pure JavaScript implementation using @noble/hashes
  • Synchronous HMAC operations
  • Works in Node.js, browsers, and edge runtimes
  • Default in otplib package

WebCryptoPlugin (Browsers)

typescript
import { crypto } from "@otplib/plugin-crypto-web";
  • Uses Web Crypto API
  • Async HMAC operations (hardware accelerated)
  • Best for browser environments with SubtleCrypto support

Error Handling

v13 introduces specific error types:

typescript
import {
  OTPError,
  SecretError,
  SecretTooShortError,
  TokenError,
  TokenLengthError,
  CryptoError,
} from "@otplib/core";

try {
  await generate({ secret: "short", crypto, base32 });
} catch (error) {
  if (error instanceof SecretTooShortError) {
    console.log("Secret must be at least 16 bytes");
  }
}

URI Generation

typescript
// v12
const uri = authenticator.keyuri("user@example.com", "MyApp", secret);

// v13 - functional
import { generateURI } from "otplib";
const uri = generateURI({
  issuer: "MyApp",
  label: "user@example.com",
  secret: "GEZDGNBVGY3TQOJQGEZDGNBVGY",
});

// v13 - class-based
const totp = new TOTP({
  issuer: "MyApp",
  label: "user@example.com",
  secret: "GEZDGNBVGY3TQOJQGEZDGNBVGY",
  crypto,
  base32,
});
const uri = totp.toURI();

TypeScript Changes

v13 provides comprehensive types:

typescript
import type {
  CryptoPlugin,
  Base32Plugin,
  HashAlgorithm,
  Digits,
  VerifyResult,
  HOTPOptions,
  TOTPOptions,
} from "@otplib/core";

// HashAlgorithm = "sha1" | "sha256" | "sha512"
// Digits = 6 | 7 | 8
// VerifyResult = { valid: boolean; delta?: number; epoch?: number }

Tolerance Changes

v13 replaces the ambiguous window option with explicit tolerance parameters.

  • TOTP: Replace window (steps) with epochTolerance (seconds).
  • HOTP: Replace window (steps) with counterTolerance (steps).

Migration Reference (TOTP)

v12 windowv13 epochTolerance (30s period)Description
00Exact match only
130±1 step (±30 seconds)
[1, 0][30, 0]Past 1 step (30s) only
260±2 steps (±60 seconds)

See Verification Tolerance for migration formulas, examples, and recommended values.

Need Help?

Released under the MIT License.