totp-js

Security-hardened TOTP for JavaScript & TypeScript. Zero dependencies, constant-time verification, replay protection.

$ npm install @authcraft/totp-js
npm version TypeScript 5.4+ Node.js 18+ RFC 6238 MIT License

Try It Live

Generate and verify TOTP codes in your browser. Same RFC 6238 algorithm as the library — running entirely client-side via the Web Crypto API.

------
--s remaining

No secrets leave your browser. Powered by the Web Crypto API.

Installation

Install via your preferred package manager. Requires Node.js 18+ with native crypto support.

npm install @authcraft/totp-js
yarn add totp-js
pnpm add totp-js

Quick Start

Generate and verify TOTP codes in four lines of code.

import { TOTP, generateSecret, Algorithm } from '@authcraft/totp-js';

// 1. Generate a secret for a new user
const secret = generateSecret(Algorithm.SHA256);

// 2. Create a TOTP instance
const totp = TOTP.defaultInstance();

// 3. Generate a code
const code = totp.generate(secret);

// 4. Verify user input
const valid = totp.verify(secret, userCode);
console.log(valid); // true or false

Replay Protection

Prevent the same TOTP code from being used twice within its validity window.

import { TOTP, InMemoryReplayGuard } from '@authcraft/totp-js';

// Create a replay guard with 2-minute retention
const replayGuard = new InMemoryReplayGuard(120_000);

// Create TOTP with replay protection
const totp = TOTP.create({ replayGuard });

// Verify with a userId for per-user tracking
const valid = totp.verify(secret, code, userId);
// Same code + same userId = rejected on second attempt

// Or auto-configure retention based on TOTP config
const guard = InMemoryReplayGuard.forConfig({ period: 30, allowedDrift: 1 });

QR Code URI

Build otpauth:// URIs for authenticator apps like Google Authenticator, Microsoft Authenticator, and Authy.

import { buildOtpauthUri, sha256Config } from '@authcraft/totp-js';

// Build an otpauth:// URI for QR code generation
const uri = buildOtpauthUri(
  secret,
  'user@example.com',
  'MyApp',
  sha256Config()
);

// Result: otpauth://totp/MyApp%3Auser%40example.com?secret=...&issuer=MyApp&algorithm=SHA256&digits=6&period=30

// Use with any QR library (e.g., qrcode npm package)
import QRCode from 'qrcode';
const dataUrl = await QRCode.toDataURL(uri);

Configuration

Use preset configurations or create fully custom ones.

Preset Configurations

import { defaultConfig, sha256Config, highSecurityConfig } from '@authcraft/totp-js';

// Google Authenticator compatible (SHA-1, 6 digits, 30s)
defaultConfig();

// Recommended for new apps (SHA-256, 6 digits, 30s)
sha256Config();

// Maximum security (SHA-512, 8 digits, 30s)
highSecurityConfig();

Custom Configuration

import { TOTP, Algorithm } from '@authcraft/totp-js';

const totp = TOTP.create({
  algorithm: Algorithm.SHA256,  // SHA1, SHA256, SHA512
  digits: 8,                    // 6-8 digits
  period: 30,                   // 15-120 seconds
  allowedDrift: 1,              // time window tolerance
});

Framework Integration

Drop-in examples for popular Node.js frameworks.

import express from 'express';
import {
  TOTP, generateSecret, buildOtpauthUri,
  Algorithm, InMemoryReplayGuard, sha256Config
} from '@authcraft/totp-js';

const app = express();
const totp = TOTP.create({
  algorithm: Algorithm.SHA256,
  replayGuard: new InMemoryReplayGuard(120_000),
});

app.post('/2fa/enroll', (req, res) => {
  const secret = generateSecret(Algorithm.SHA256);
  // Store secret in database (encrypted)
  const uri = buildOtpauthUri(secret, req.user.email, 'MyApp', sha256Config());
  res.json({ secret, uri });
});

app.post('/2fa/verify', (req, res) => {
  const { code } = req.body;
  const secret = getUserSecret(req.user.id); // from DB
  const valid = totp.verify(secret, code, req.user.id);
  res.json({ valid });
});
// app/api/2fa/verify/route.ts
import { NextResponse } from 'next/server';
import { TOTP, Algorithm, InMemoryReplayGuard } from '@authcraft/totp-js';

const totp = TOTP.create({
  algorithm: Algorithm.SHA256,
  replayGuard: new InMemoryReplayGuard(120_000),
});

export async function POST(request: Request) {
  const { code, userId } = await request.json();
  const secret = await getUserSecret(userId);
  const valid = totp.verify(secret, code, userId);
  return NextResponse.json({ valid });
}
import { Injectable } from '@nestjs/common';
import {
  TOTP, generateSecret, buildOtpauthUri,
  Algorithm, InMemoryReplayGuard, sha256Config
} from '@authcraft/totp-js';

@Injectable()
export class TwoFactorService {
  private readonly totp = TOTP.create({
    algorithm: Algorithm.SHA256,
    replayGuard: new InMemoryReplayGuard(120_000),
  });

  enrollUser(email: string): { secret: string; uri: string } {
    const secret = generateSecret(Algorithm.SHA256);
    const uri = buildOtpauthUri(secret, email, 'MyApp', sha256Config());
    return { secret, uri };
  }

  verifyCode(userId: string, code: string, secret: string): boolean {
    return this.totp.verify(secret, code, userId);
  }
}
import Fastify from 'fastify';
import {
  TOTP, generateSecret, buildOtpauthUri,
  Algorithm, InMemoryReplayGuard, sha256Config
} from '@authcraft/totp-js';

const app = Fastify();
const totp = TOTP.create({
  algorithm: Algorithm.SHA256,
  replayGuard: new InMemoryReplayGuard(120_000),
});

app.post('/2fa/enroll', async (request, reply) => {
  const secret = generateSecret(Algorithm.SHA256);
  const uri = buildOtpauthUri(secret, request.user.email, 'MyApp', sha256Config());
  return { secret, uri };
});

app.post('/2fa/verify', async (request, reply) => {
  const { code } = request.body as { code: string };
  const secret = await getUserSecret(request.user.id);
  const valid = totp.verify(secret, code, request.user.id);
  return { valid };
});

Secret Generation

Generate cryptographically secure secrets sized appropriately for each algorithm.

import { generateSecret, generateSecretBytes, isValidSecret, Algorithm } from '@authcraft/totp-js';

// Generate for a specific algorithm (recommended)
const sha1Secret   = generateSecret(Algorithm.SHA1);    // 20 bytes / 160 bits
const sha256Secret = generateSecret(Algorithm.SHA256);  // 32 bytes / 256 bits
const sha512Secret = generateSecret(Algorithm.SHA512);  // 64 bytes / 512 bits

// Or specify exact byte length
const secret = generateSecretBytes(32);

// Validate a Base32 secret
const valid = isValidSecret(secret); // true

Features

RFC Compliant

Full compliance with TOTP (RFC 6238) and HOTP (RFC 4226) standards. Tested against reference vectors.

Zero Dependencies

No runtime dependencies. Uses Node.js built-in crypto module for all cryptographic operations.

🛡

Replay Protection

Built-in InMemoryReplayGuard prevents the same code from being used twice. Pluggable via the ReplayGuard interface.

Constant-Time Verification

All code comparisons use constant-time algorithms to prevent timing side-channel attacks.

TypeScript Native

Written in TypeScript with full type definitions. Supports ESM and CommonJS out of the box.

Preset Configs

Ready-to-use presets: defaultConfig, sha256Config, and highSecurityConfig.

API Reference

TOTP

MethodDescription
TOTP.create(options?)Create instance with custom options (algorithm, digits, period, drift, replayGuard)
TOTP.defaultInstance()Create instance with default config (SHA-1, 6 digits, 30s)
generate(secret)Generate TOTP code for the current time
generateAt(secret, timestamp)Generate code for a specific Unix timestamp (ms)
verify(secret, code, userId?)Verify a code. Returns boolean. Includes replay protection when userId provided.
verifyWithDetails(secret, code, userId?)Verify with detailed result: { valid, timeOffset, message }
getSecondsRemaining()Seconds until current code expires
getCurrentCounter()Current TOTP time counter value

Configuration Presets

FunctionAlgorithmDigitsPeriodUse Case
defaultConfig()SHA-1630sGoogle Authenticator compatible
sha256Config()SHA-256630sRecommended for new apps
highSecurityConfig()SHA-512830sMaximum security
createConfig(opts)Create custom config with validation (period 15-120s, digits 6-8, drift 0-5)

Algorithms

AlgorithmKey SizeConstantUse Case
SHA-120 bytes (160 bits)Algorithm.SHA1Legacy / Google Authenticator
SHA-25632 bytes (256 bits)Algorithm.SHA256Recommended
SHA-51264 bytes (512 bits)Algorithm.SHA512Maximum security

Utility Functions

FunctionDescription
generateSecret(algorithm?)Generate Base32-encoded secret sized for the given algorithm
generateSecretBytes(length?)Generate Base32 secret with exact byte length (min 16)
generateRawSecret(length?)Generate raw Uint8Array secret bytes
isValidSecret(secret)Validate a Base32 secret string
buildOtpauthUri(secret, account, issuer, config?)Build otpauth:// URI for QR codes
algorithmFromName(name)Look up algorithm by string name (e.g. "SHA256")

Security

  • Constant-time comparison — All TOTP code verifications use constant-time equality checks to prevent timing side-channel attacks.
  • Replay attack prevention — Built-in InMemoryReplayGuard with auto-expiry, or implement the ReplayGuard interface for Redis/database-backed stores.
  • Input validation — All secrets are validated as proper Base32 before use. Configuration values are bounds-checked.
  • Drift-aware verification — Configurable time window tolerance (allowedDrift) to handle clock skew between client and server.
  • Zero runtime dependencies — No third-party code in the dependency tree means a smaller attack surface.
  • Node.js crypto — All cryptographic operations use the built-in node:crypto module backed by OpenSSL.
  • Typed error handling — Custom TOTPError with ErrorCode enum for programmatic error handling.