CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-otplib

HMAC-based (HOTP) and Time-based (TOTP) One-Time Password library compatible with Google Authenticator

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

hotp.mddocs/

HOTP (HMAC-based One-Time Password)

HOTP implements RFC 4226 counter-based one-time passwords. Unlike time-based systems, HOTP uses an incrementing counter that must be synchronized between client and server.

Capabilities

Token Generation

Generates tokens based on a secret key and counter value.

/**
 * Generate an HOTP token using secret and counter
 * @param secret - Secret key in the configured encoding
 * @param counter - Counter value (must be synchronized)
 * @returns Token string with configured digit length
 */
generate(secret: string, counter: number): string;

Usage Example:

import { hotp } from "otplib";

const secret = "your-secret-key";
let counter = 0;

// Generate tokens with incrementing counter
const token1 = hotp.generate(secret, counter++);
const token2 = hotp.generate(secret, counter++);
const token3 = hotp.generate(secret, counter++);

console.log(token1); // "123456"
console.log(token2); // "789012"
console.log(token3); // "345678"

// Configure custom options
hotp.options = { digits: 8, algorithm: 'sha256' };
const longToken = hotp.generate(secret, counter++);
console.log(longToken); // "12345678"

Token Verification

Verifies tokens against a specific counter value. The counter must match exactly.

/**
 * Verify an HOTP token against secret and counter
 * @param token - Token to verify
 * @param secret - Secret key
 * @param counter - Expected counter value
 * @returns true if token matches the generated token for this counter
 */
check(token: string, secret: string, counter: number): boolean;

/**
 * Object-based token verification
 * @param opts - Object containing token, secret, and counter
 * @returns true if token is valid for the given counter
 */
verify(opts: { token: string; secret: string; counter: number }): boolean;

Usage Examples:

import { hotp } from "otplib";

const secret = "your-secret-key";
const counter = 5;
const token = "123456";

// Method 1: Direct parameters
const isValid = hotp.check(token, secret, counter);

// Method 2: Object parameters
const isValid2 = hotp.verify({ token, secret, counter });

console.log(isValid); // true or false

// Counter synchronization example
let serverCounter = 0;
let maxLookAhead = 10;

function verifyHotpWithSync(token: string, secret: string): boolean {
  // Try current counter and look ahead
  for (let i = 0; i <= maxLookAhead; i++) {
    if (hotp.check(token, secret, serverCounter + i)) {
      serverCounter = serverCounter + i + 1; // Update for next token
      return true;
    }
  }
  return false;
}

QR Code URI Generation

Generate otpauth:// URIs for HOTP setup with counter information.

/**
 * Generate an otpauth URI for HOTP setup
 * @param accountName - User identifier
 * @param issuer - Service name  
 * @param secret - Secret key
 * @param counter - Initial counter value
 * @returns otpauth://hotp/ URI string
 */
keyuri(accountName: string, issuer: string, secret: string, counter: number): string;

Usage Example:

import { hotp } from "otplib";

const secret = "your-secret-key";
const user = "user@example.com";
const service = "My Service";
const initialCounter = 0;

const otpauth = hotp.keyuri(user, service, secret, initialCounter);
console.log(otpauth);
// "otpauth://hotp/My%20Service:user@example.com?secret=your-secret-key&counter=0&issuer=My%20Service"

Configuration Management

Manage HOTP-specific configuration options.

/**
 * Get/set configuration options
 */
options: Partial<HOTPOptions>;

/**
 * Reset options to default values
 */
resetOptions(): void;

/**
 * Get all options with defaults applied
 * @returns Complete options object
 */
allOptions(): Readonly<HOTPOptions>;

/**
 * Create new instance with custom defaults
 * @param defaultOptions - Custom default options
 * @returns New HOTP instance
 */
create(defaultOptions?: Partial<HOTPOptions>): HOTP;

Usage Examples:

import { hotp } from "otplib";

// Configure options
hotp.options = {
  digits: 8,           // 8-digit tokens
  algorithm: 'sha256', // SHA-256 HMAC
  encoding: 'hex'      // Hex-encoded secrets
};

// Create instance with custom defaults
const customHotp = hotp.create({
  digits: 6,
  algorithm: 'sha1',
  encoding: 'ascii'
});

// Reset to library defaults
hotp.resetOptions();

// View complete configuration
const config = hotp.allOptions();
console.log(config.digits); // 6
console.log(config.algorithm); // 'sha1'

Counter Management Patterns

Simple Counter Tracking

import { hotp } from "otplib";

class HotpService {
  private counters = new Map<string, number>();

  generateToken(userId: string, secret: string): string {
    const counter = this.getCounter(userId);
    const token = hotp.generate(secret, counter);
    this.incrementCounter(userId);
    return token;
  }

  verifyToken(userId: string, secret: string, token: string): boolean {
    const counter = this.getCounter(userId);
    return hotp.check(token, secret, counter);
  }

  private getCounter(userId: string): number {
    return this.counters.get(userId) || 0;
  }

  private incrementCounter(userId: string): void {
    const current = this.getCounter(userId);
    this.counters.set(userId, current + 1);
  }
}

Counter Synchronization with Look-ahead

import { hotp } from "otplib";

class HotpSyncService {
  private counters = new Map<string, number>();
  private readonly LOOK_AHEAD_WINDOW = 10;

  verifyTokenWithSync(userId: string, secret: string, token: string): boolean {
    const baseCounter = this.getCounter(userId);
    
    // Try current counter and look ahead
    for (let i = 0; i <= this.LOOK_AHEAD_WINDOW; i++) {
      const testCounter = baseCounter + i;
      if (hotp.check(token, secret, testCounter)) {
        // Update counter to one past the successful counter
        this.setCounter(userId, testCounter + 1);
        return true;
      }
    }
    
    return false;
  }

  private getCounter(userId: string): number {
    return this.counters.get(userId) || 0;
  }

  private setCounter(userId: string, counter: number): void {
    this.counters.set(userId, counter);
  }
}

Hardware Token Simulation

import { hotp } from "otplib";

class HardwareTokenSimulator {
  private counter: number = 0;
  
  constructor(private secret: string) {}

  // Simulate pressing the button on a hardware token
  pressButton(): string {
    const token = hotp.generate(this.secret, this.counter);
    this.counter++;
    return token;
  }

  // Reset counter (usually requires physical reset)
  resetCounter(newCounter: number = 0): void {
    this.counter = newCounter;
  }

  getCurrentCounter(): number {
    return this.counter;
  }
}

// Usage
const token = new HardwareTokenSimulator("shared-secret");
const firstToken = token.pressButton();  // Uses counter 0
const secondToken = token.pressButton(); // Uses counter 1

Types

interface HOTPOptions {
  /** HMAC algorithm to use */
  algorithm: 'sha1' | 'sha256' | 'sha512';
  /** Number of digits in generated token */
  digits: number;
  /** Encoding format of the secret key */
  encoding: 'ascii' | 'base64' | 'hex' | 'latin1' | 'utf8';
  /** Function to create HMAC digest */
  createDigest: (algorithm: string, key: string, data: string) => string;
  /** Function to create HMAC key from secret */
  createHmacKey: (algorithm: string, secret: string, encoding: string) => string;
  /** Pre-computed digest (advanced usage) */
  digest?: string;
}

Install with Tessl CLI

npx tessl i tessl/npm-otplib

docs

authenticator.md

configuration.md

hotp.md

index.md

presets.md

totp.md

tile.json