npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

2fa-lib

v0.1.2

Published

Tiny zero-dependency TOTP and HOTP library for Node.js, browsers, Deno, Bun, Cloudflare Workers and edge runtimes. RFC 6238 / RFC 4226 compliant. Google Authenticator compatible two-factor authentication (2FA / MFA) for JavaScript and TypeScript.

Readme

2fa-lib

Tiny, zero-dependency TOTP & HOTP library for JavaScript and TypeScript

Two-Factor Authentication (2FA / MFA) made simple. Works with Google Authenticator, Microsoft Authenticator, Authy, 1Password, Bitwarden, Duo, and any RFC 6238 compatible app.

npm version npm downloads bundle size license types github stars

InstallQuick StartAPIExamplesCompatibilityWhy 2fa-lib?


✨ Features

  • 🪶 Tiny — under 5 KB minified, single file
  • 🚫 Zero dependencies — uses native Web Crypto API
  • 🌍 Universal — Node.js 18+, browsers, Deno, Bun, Cloudflare Workers, Vercel Edge, Netlify Edge
  • 🔒 Secure — constant-time comparison, cryptographically secure secrets
  • 📘 TypeScript — full type definitions included, no @types package needed
  • RFC compliant — RFC 6238 (TOTP) and RFC 4226 (HOTP), tested against official vectors
  • 📱 Universal app support — Google Authenticator, Microsoft Authenticator, Authy, 1Password, Bitwarden, Duo, FreeOTP
  • 🛡️ Built-in security helpers — rate limiter, replay guard, hashed backup codes
  • 🔄 QR code URI — generates otpauth:// URIs for any QR library
  • 🎮 Steam Guard — Steam's 5-character format supported
  • 📥 Import from QR — parse otpauth:// URIs from other apps
  • 💎 ESM + CJS — works with import and require
  • 🆕 Backup codes — generate + securely hash recovery codes

📦 Installation

npm install 2fa-lib
yarn add 2fa-lib
pnpm add 2fa-lib
bun add 2fa-lib

🚀 Quick Start

import { generateSecret, totp, verify, buildURI } from '2fa-lib';

// 1. Create a secret for the user (store it encrypted in your DB)
const secret = generateSecret();

// 2. Build a provisioning URI → render as QR code in your UI
const uri = buildURI({
  secret,
  label: '[email protected]',
  issuer: 'MyApp',
});

// 3. Generate the current 6-digit code
const code = await totp(secret);
console.log(code); // → "123456"

// 4. Verify a user-submitted code (allows ±1 step clock drift)
const result = await verify('123456', secret, { window: 1 });
if (result) {
  console.log('✅ Valid! Drift:', result.delta);
} else {
  console.log('❌ Invalid code');
}

📘 TypeScript

Full TypeScript types are bundled — no extra @types/2fa-lib package required.

import { totp, verify, type VerifyResult } from '2fa-lib';

const code: string = await totp('JBSWY3DPEHPK3PXP');
const result: VerifyResult | null = await verify(code, 'JBSWY3DPEHPK3PXP');

📱 Authenticator App Compatibility

| App | Status | Notes | |---|---|---| | Google Authenticator | ✅ Full | All algorithms supported | | Microsoft Authenticator | ✅ Full | Use microsoftAuthenticatorURI() (forces SHA1/6/30) | | Authy | ✅ Full | All algorithms supported | | 1Password | ✅ Full | All algorithms supported | | Bitwarden | ✅ Full | All algorithms supported | | Duo Mobile | ✅ Full | Standard TOTP | | FreeOTP / FreeOTP+ | ✅ Full | All algorithms supported | | Steam Guard | ✅ Full | Use steamTOTP() for 5-char codes |

⚠️ Microsoft Authenticator silently ignores algorithm, digits, and period URI parameters — it always uses SHA1, 6 digits, 30 seconds. Use the dedicated microsoftAuthenticatorURI() helper to guarantee compatibility.


📚 API

Core

totp(secret, options?)

Generate a time-based one-time password (TOTP).

await totp('JBSWY3DPEHPK3PXP');
await totp(secret, { digits: 8, period: 60, algorithm: 'SHA256' });

hotp(secret, counter, options?)

Generate an HMAC-based one-time password (HOTP).

await hotp('JBSWY3DPEHPK3PXP', 0); // → "755224"

verify(token, secret, options?)

Verify a token. Returns { delta } or null. The window option allows ±N steps of clock drift (default 1 = ±30s).

const result = await verify('123456', secret, { window: 1 });
if (result) console.log('Valid, drift:', result.delta);

generateSecret(length?)

Generate a cryptographically secure base32-encoded secret. Default length: 20 bytes.

const secret = generateSecret(); // → "JBSWY3DPEHPK3PXP..."

timeRemaining(options?)

Seconds remaining in the current TOTP step. Useful for countdown UIs.

console.log(`${timeRemaining()}s until next code`);

totpPair(secret, options?)

Returns the current and next TOTP codes — useful for "next code" UI hints.

const { current, next } = await totpPair(secret);

QR Code Provisioning

buildURI(options)

Build an otpauth:// URI for QR code provisioning.

const uri = buildURI({
  secret,
  label: '[email protected]',
  issuer: 'MyApp',
  algorithm: 'SHA1', // 'SHA1' | 'SHA256' | 'SHA512'
  digits: 6,
  period: 30,
});

microsoftAuthenticatorURI({ secret, label, issuer })

Build a URI guaranteed to work with Microsoft Authenticator.

const uri = microsoftAuthenticatorURI({
  secret,
  label: '[email protected]',
  issuer: 'MyApp',
});

parseURI(uri)

Parse an otpauth:// URI into its components — perfect for importing secrets from QR codes.

const parsed = parseURI('otpauth://totp/MyApp:alice?secret=...&issuer=MyApp');
// { type, label, issuer, secret, algorithm, digits, period, counter? }

Security Helpers

generateBackupCodes(count?, length?)

Generate human-friendly single-use recovery codes (no confusing characters).

const codes = generateBackupCodes(10, 8); // 10 codes, 8 chars each

hashBackupCode(code) & verifyBackupCode(code, hashes)

Store backup codes as SHA-256 hashes in your database, never plaintext.

const codes = generateBackupCodes();
const hashes = await Promise.all(codes.map(hashBackupCode));
// store `hashes` in DB

// Later...
const idx = await verifyBackupCode(userInput, hashes);
if (idx !== -1) {
  // Valid! Remove hashes[idx] so it can't be reused
}

createRateLimiter({ max, windowMs })

Built-in brute-force protection.

const limiter = createRateLimiter({ max: 5, windowMs: 60_000 });

if (!limiter.allow(userId)) {
  throw new Error('Too many attempts. Try again later.');
}

createReplayGuard()

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

const guard = createReplayGuard();

const result = await verify(token, secret);
if (!result) throw new Error('Invalid');

const step = Math.floor(Date.now() / 1000 / 30) + result.delta;
if (!guard.accept(userId, step)) {
  throw new Error('Token already used');
}

isValidTokenFormat(token, options?)

Pre-validate token shape before calling verify().

if (!isValidTokenFormat(input)) return res.status(400).send('Bad format');

Steam Guard

steamTOTP(secret, options?)

Generate a 5-character Steam Guard code.

const code = await steamTOTP(secret); // → "K8R3F"

💡 Examples

Express.js — Enable 2FA for a user

import express from 'express';
import QRCode from 'qrcode';
import { generateSecret, buildURI, verify } from '2fa-lib';

const app = express();

app.post('/2fa/setup', async (req, res) => {
  const secret = generateSecret();
  await db.users.update(req.user.id, { totpSecret: secret, totpEnabled: false });

  const uri = buildURI({
    secret,
    label: req.user.email,
    issuer: 'MyApp',
  });

  const qrDataUrl = await QRCode.toDataURL(uri);
  res.json({ qrDataUrl, secret });
});

app.post('/2fa/verify', async (req, res) => {
  const user = await db.users.findById(req.user.id);
  const result = await verify(req.body.code, user.totpSecret, { window: 1 });

  if (!result) return res.status(401).json({ error: 'Invalid code' });

  await db.users.update(user.id, { totpEnabled: true });
  res.json({ success: true });
});

Next.js App Router — Login with 2FA

// app/api/login/route.ts
import { verify, createRateLimiter } from '2fa-lib';

const limiter = createRateLimiter({ max: 5, windowMs: 60_000 });

export async function POST(req: Request) {
  const { email, password, code } = await req.json();

  if (!limiter.allow(email)) {
    return Response.json({ error: 'Too many attempts' }, { status: 429 });
  }

  const user = await db.users.findByEmail(email);
  // ... validate password ...

  if (user.totpEnabled) {
    const result = await verify(code, user.totpSecret);
    if (!result) return Response.json({ error: 'Invalid 2FA code' }, { status: 401 });
  }

  return Response.json({ token: createSessionToken(user) });
}

Cloudflare Workers — Edge runtime

import { totp, verify } from '2fa-lib';

export default {
  async fetch(request, env) {
    const code = await totp(env.USER_SECRET);
    return new Response(code);
  },
};

🤔 Why 2fa-lib?

| Feature | 2fa-lib | otplib | speakeasy | otpauth | |---|:---:|:---:|:---:|:---:| | Zero dependencies | ✅ | ❌ | ❌ | ✅ | | TypeScript types built-in | ✅ | ✅ | ❌ | ✅ | | Edge runtime support | ✅ | ⚠️ | ❌ | ✅ | | Microsoft Authenticator helper | ✅ | ❌ | ❌ | ❌ | | otpauth:// URI parser | ✅ | ❌ | ❌ | ✅ | | Backup code hashing | ✅ | ❌ | ❌ | ❌ | | Built-in rate limiter | ✅ | ❌ | ❌ | ❌ | | Replay protection | ✅ | ❌ | ❌ | ❌ | | Steam Guard | ✅ | ❌ | ❌ | ❌ | | Bundle size (min+gzip) | <5 KB | ~15 KB | ~30 KB | ~8 KB |


🔐 Security Best Practices

  1. Encrypt secrets at rest. Never store TOTP secrets in plaintext in your database.
  2. Use the rate limiter. Brute-forcing 6 digits takes ~1M attempts on average — enforce limits.
  3. Use the replay guard. Don't allow the same code to be used twice.
  4. Hash backup codes. Never store them in plaintext.
  5. Use HTTPS. Always.
  6. Allow drift. Use window: 1 (±30s) to handle clock skew between server and phone.

🧪 Testing

npm test

The library is tested against the official RFC 4226 and RFC 6238 test vectors.


📖 Standards


🤝 Contributing

PRs welcome! Please open an issue first for major changes.


📄 License

MIT © ai-mehedi


Keywords

totp · hotp · 2fa · mfa · two-factor authentication · multi-factor authentication google authenticator · microsoft authenticator · authy · 1password · bitwarden one-time password · rfc 6238 · rfc 4226 · otpauth · qr code · authentication security · nodejs · typescript · deno · bun · cloudflare workers · edge zero dependencies · lightweight · tiny · esm · web crypto · hmac

If you find this useful, please star the repo on GitHub!