{"_id":"2fa-lib","_rev":"2-8e502094919ef582fc4affe427037653","name":"2fa-lib","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"2fa-lib","version":"0.1.0","keywords":["totp","hotp","otp","2fa","mfa","two-factor","two-factor-authentication","multi-factor","multi-factor-authentication","authenticator","google-authenticator","authy","1password","rfc6238","rfc4226","rfc-6238","rfc-4226","one-time-password","one-time-passcode","time-based","time-based-otp","hmac","hmac-otp","qrcode","qr-code","otpauth","security","auth","authentication","login","verify","totp-generator","totp-verify","node","browser","deno","bun","edge","cloudflare-workers","vercel-edge","typescript","esm","zero-dependencies","lightweight","tiny"],"author":{"name":"ai-mehedi"},"license":"MIT","_id":"2fa-lib@0.1.0","maintainers":[{"name":"ai-mehedi","email":"mdaminulislamdev23@gmail.com"}],"homepage":"https://github.com/ai-mehedi/2fa-lib#readme","bugs":{"url":"https://github.com/ai-mehedi/2fa-lib/issues"},"dist":{"shasum":"5362fc7bc1461d87fd189e1eb5c6bb723a9bb92b","tarball":"https://registry.npmjs.org/2fa-lib/-/2fa-lib-0.1.0.tgz","fileCount":6,"integrity":"sha512-BMdLCG4WVuptFuNRtgdhFItSdO/YcZxKXwPQnmxGYdfMIwk/OmQLn/6ro/1MbBGYqliBcikiYzcyjH0vFklFkw==","signatures":[{"sig":"MEUCIQDw3T3QlTmcZTFgdfnyYnLznhJ19QFdlUhLB5BOCF0zHQIgDoqaga1LeOGvNnMyzJ8QBxygxgA2WVW8PdKMNZHkHLo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":30204},"main":"./src/index.js","type":"module","types":"./src/index.d.ts","module":"./src/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./src/index.d.ts","import":"./src/index.js","default":"./src/index.js"}},"gitHead":"cfc3530a893e3b2a6a00c1ca3cf8ed3a25cbc391","scripts":{"test":"node --test test/totp.test.js"},"_npmUser":{"name":"ai-mehedi","email":"mdaminulislamdev23@gmail.com"},"repository":{"url":"git+https://github.com/ai-mehedi/2fa-lib.git","type":"git"},"_npmVersion":"11.4.1","description":"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.","directories":{},"_nodeVersion":"22.13.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/2fa-lib_0.1.0_1775674523030_0.3158138346680146","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"2fa-lib","version":"0.1.2","description":"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.","type":"module","main":"./src/index.js","module":"./src/index.js","types":"./src/index.d.ts","exports":{".":{"types":"./src/index.d.ts","import":"./src/index.js","default":"./src/index.js"}},"scripts":{"test":"node --test test/totp.test.js"},"keywords":["totp","hotp","otp","2fa","mfa","two-factor","two-factor-authentication","multi-factor","multi-factor-authentication","authenticator","google-authenticator","authy","1password","rfc6238","rfc4226","rfc-6238","rfc-4226","one-time-password","one-time-passcode","time-based","time-based-otp","hmac","hmac-otp","qrcode","qr-code","otpauth","security","auth","authentication","login","verify","totp-generator","totp-verify","node","browser","deno","bun","edge","cloudflare-workers","vercel-edge","typescript","esm","zero-dependencies","lightweight","tiny"],"author":{"name":"ai-mehedi"},"license":"MIT","homepage":"https://github.com/ai-mehedi/2fa-lib#readme","repository":{"type":"git","url":"git+https://github.com/ai-mehedi/2fa-lib.git"},"bugs":{"url":"https://github.com/ai-mehedi/2fa-lib/issues"},"engines":{"node":">=18"},"_id":"2fa-lib@0.1.2","gitHead":"76c35f3f8bb5537e4fa8ee51c896544949308745","_nodeVersion":"22.13.0","_npmVersion":"11.4.1","dist":{"integrity":"sha512-SgimcKfpdOG85pibehvpXDxbXD4QxTfxCzm0kj08z4xTQR6cjlO7z9OU6pLDPIGVPmcuXZs9NIaYUjsyzBVlgQ==","shasum":"f09779aef30e24d4e7459e93f3477c892945dd9e","tarball":"https://registry.npmjs.org/2fa-lib/-/2fa-lib-0.1.2.tgz","fileCount":6,"unpackedSize":30204,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCID3NmmlK5bIsF96/kM2aMjqqh4/2FJmQoLPf4C8i62S4AiA+ws6OlRAfCSJaLhmMD63vYQWDRlQkeV9FUeqRdpSUxw=="}]},"_npmUser":{"name":"ai-mehedi","email":"mdaminulislamdev23@gmail.com"},"directories":{},"maintainers":[{"name":"ai-mehedi","email":"mdaminulislamdev23@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/2fa-lib_0.1.2_1775675035446_0.0047298778017939025"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-08T18:55:23.029Z","modified":"2026-04-08T19:03:55.697Z","0.1.0":"2026-04-08T18:55:23.200Z","0.1.2":"2026-04-08T19:03:55.599Z"},"bugs":{"url":"https://github.com/ai-mehedi/2fa-lib/issues"},"author":{"name":"ai-mehedi"},"license":"MIT","homepage":"https://github.com/ai-mehedi/2fa-lib#readme","keywords":["totp","hotp","otp","2fa","mfa","two-factor","two-factor-authentication","multi-factor","multi-factor-authentication","authenticator","google-authenticator","authy","1password","rfc6238","rfc4226","rfc-6238","rfc-4226","one-time-password","one-time-passcode","time-based","time-based-otp","hmac","hmac-otp","qrcode","qr-code","otpauth","security","auth","authentication","login","verify","totp-generator","totp-verify","node","browser","deno","bun","edge","cloudflare-workers","vercel-edge","typescript","esm","zero-dependencies","lightweight","tiny"],"repository":{"type":"git","url":"git+https://github.com/ai-mehedi/2fa-lib.git"},"description":"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.","maintainers":[{"name":"ai-mehedi","email":"mdaminulislamdev23@gmail.com"}],"readme":"<div align=\"center\">\n\n# 2fa-lib\n\n### Tiny, zero-dependency TOTP & HOTP library for JavaScript and TypeScript\n\n**Two-Factor Authentication (2FA / MFA) made simple.**\nWorks with Google Authenticator, Microsoft Authenticator, Authy, 1Password, Bitwarden, Duo, and any RFC 6238 compatible app.\n\n[![npm version](https://img.shields.io/npm/v/2fa-lib.svg?style=flat-square)](https://www.npmjs.com/package/2fa-lib)\n[![npm downloads](https://img.shields.io/npm/dm/2fa-lib.svg?style=flat-square)](https://www.npmjs.com/package/2fa-lib)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/2fa-lib?style=flat-square)](https://bundlephobia.com/package/2fa-lib)\n[![license](https://img.shields.io/npm/l/2fa-lib.svg?style=flat-square)](./LICENSE)\n[![types](https://img.shields.io/npm/types/2fa-lib.svg?style=flat-square)](./src/index.d.ts)\n[![github stars](https://img.shields.io/github/stars/ai-mehedi/2fa-lib?style=flat-square)](https://github.com/ai-mehedi/2fa-lib)\n\n[Install](#-installation) • [Quick Start](#-quick-start) • [API](#-api) • [Examples](#-examples) • [Compatibility](#-authenticator-app-compatibility) • [Why 2fa-lib?](#-why-2fa-lib)\n\n</div>\n\n---\n\n## ✨ Features\n\n- 🪶 **Tiny** — under 5 KB minified, single file\n- 🚫 **Zero dependencies** — uses native Web Crypto API\n- 🌍 **Universal** — Node.js 18+, browsers, Deno, Bun, Cloudflare Workers, Vercel Edge, Netlify Edge\n- 🔒 **Secure** — constant-time comparison, cryptographically secure secrets\n- 📘 **TypeScript** — full type definitions included, no `@types` package needed\n- ✅ **RFC compliant** — RFC 6238 (TOTP) and RFC 4226 (HOTP), tested against official vectors\n- 📱 **Universal app support** — Google Authenticator, **Microsoft Authenticator**, Authy, 1Password, Bitwarden, Duo, FreeOTP\n- 🛡️ **Built-in security helpers** — rate limiter, replay guard, hashed backup codes\n- 🔄 **QR code URI** — generates `otpauth://` URIs for any QR library\n- 🎮 **Steam Guard** — Steam's 5-character format supported\n- 📥 **Import from QR** — parse `otpauth://` URIs from other apps\n- 💎 **ESM + CJS** — works with `import` and `require`\n- 🆕 **Backup codes** — generate + securely hash recovery codes\n\n---\n\n## 📦 Installation\n\n```bash\nnpm install 2fa-lib\n```\n\n```bash\nyarn add 2fa-lib\n```\n\n```bash\npnpm add 2fa-lib\n```\n\n```bash\nbun add 2fa-lib\n```\n\n---\n\n## 🚀 Quick Start\n\n```js\nimport { generateSecret, totp, verify, buildURI } from '2fa-lib';\n\n// 1. Create a secret for the user (store it encrypted in your DB)\nconst secret = generateSecret();\n\n// 2. Build a provisioning URI → render as QR code in your UI\nconst uri = buildURI({\n  secret,\n  label: 'alice@example.com',\n  issuer: 'MyApp',\n});\n\n// 3. Generate the current 6-digit code\nconst code = await totp(secret);\nconsole.log(code); // → \"123456\"\n\n// 4. Verify a user-submitted code (allows ±1 step clock drift)\nconst result = await verify('123456', secret, { window: 1 });\nif (result) {\n  console.log('✅ Valid! Drift:', result.delta);\n} else {\n  console.log('❌ Invalid code');\n}\n```\n\n---\n\n## 📘 TypeScript\n\nFull TypeScript types are bundled — no extra `@types/2fa-lib` package required.\n\n```ts\nimport { totp, verify, type VerifyResult } from '2fa-lib';\n\nconst code: string = await totp('JBSWY3DPEHPK3PXP');\nconst result: VerifyResult | null = await verify(code, 'JBSWY3DPEHPK3PXP');\n```\n\n---\n\n## 📱 Authenticator App Compatibility\n\n| App | Status | Notes |\n|---|---|---|\n| **Google Authenticator** | ✅ Full | All algorithms supported |\n| **Microsoft Authenticator** | ✅ Full | Use `microsoftAuthenticatorURI()` (forces SHA1/6/30) |\n| **Authy** | ✅ Full | All algorithms supported |\n| **1Password** | ✅ Full | All algorithms supported |\n| **Bitwarden** | ✅ Full | All algorithms supported |\n| **Duo Mobile** | ✅ Full | Standard TOTP |\n| **FreeOTP / FreeOTP+** | ✅ Full | All algorithms supported |\n| **Steam Guard** | ✅ Full | Use `steamTOTP()` for 5-char codes |\n\n> ⚠️ **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.\n\n---\n\n## 📚 API\n\n### Core\n\n#### `totp(secret, options?)`\nGenerate a time-based one-time password (TOTP).\n\n```js\nawait totp('JBSWY3DPEHPK3PXP');\nawait totp(secret, { digits: 8, period: 60, algorithm: 'SHA256' });\n```\n\n#### `hotp(secret, counter, options?)`\nGenerate an HMAC-based one-time password (HOTP).\n\n```js\nawait hotp('JBSWY3DPEHPK3PXP', 0); // → \"755224\"\n```\n\n#### `verify(token, secret, options?)`\nVerify a token. Returns `{ delta }` or `null`. The `window` option allows ±N steps of clock drift (default 1 = ±30s).\n\n```js\nconst result = await verify('123456', secret, { window: 1 });\nif (result) console.log('Valid, drift:', result.delta);\n```\n\n#### `generateSecret(length?)`\nGenerate a cryptographically secure base32-encoded secret. Default length: 20 bytes.\n\n```js\nconst secret = generateSecret(); // → \"JBSWY3DPEHPK3PXP...\"\n```\n\n#### `timeRemaining(options?)`\nSeconds remaining in the current TOTP step. Useful for countdown UIs.\n\n```js\nconsole.log(`${timeRemaining()}s until next code`);\n```\n\n#### `totpPair(secret, options?)`\nReturns the current and next TOTP codes — useful for \"next code\" UI hints.\n\n```js\nconst { current, next } = await totpPair(secret);\n```\n\n### QR Code Provisioning\n\n#### `buildURI(options)`\nBuild an `otpauth://` URI for QR code provisioning.\n\n```js\nconst uri = buildURI({\n  secret,\n  label: 'alice@example.com',\n  issuer: 'MyApp',\n  algorithm: 'SHA1', // 'SHA1' | 'SHA256' | 'SHA512'\n  digits: 6,\n  period: 30,\n});\n```\n\n#### `microsoftAuthenticatorURI({ secret, label, issuer })`\nBuild a URI guaranteed to work with **Microsoft Authenticator**.\n\n```js\nconst uri = microsoftAuthenticatorURI({\n  secret,\n  label: 'alice@example.com',\n  issuer: 'MyApp',\n});\n```\n\n#### `parseURI(uri)`\nParse an `otpauth://` URI into its components — perfect for importing secrets from QR codes.\n\n```js\nconst parsed = parseURI('otpauth://totp/MyApp:alice?secret=...&issuer=MyApp');\n// { type, label, issuer, secret, algorithm, digits, period, counter? }\n```\n\n### Security Helpers\n\n#### `generateBackupCodes(count?, length?)`\nGenerate human-friendly single-use recovery codes (no confusing characters).\n\n```js\nconst codes = generateBackupCodes(10, 8); // 10 codes, 8 chars each\n```\n\n#### `hashBackupCode(code)` & `verifyBackupCode(code, hashes)`\nStore backup codes as **SHA-256 hashes** in your database, never plaintext.\n\n```js\nconst codes = generateBackupCodes();\nconst hashes = await Promise.all(codes.map(hashBackupCode));\n// store `hashes` in DB\n\n// Later...\nconst idx = await verifyBackupCode(userInput, hashes);\nif (idx !== -1) {\n  // Valid! Remove hashes[idx] so it can't be reused\n}\n```\n\n#### `createRateLimiter({ max, windowMs })`\nBuilt-in brute-force protection.\n\n```js\nconst limiter = createRateLimiter({ max: 5, windowMs: 60_000 });\n\nif (!limiter.allow(userId)) {\n  throw new Error('Too many attempts. Try again later.');\n}\n```\n\n#### `createReplayGuard()`\nPrevent the same TOTP code from being used twice within its valid window.\n\n```js\nconst guard = createReplayGuard();\n\nconst result = await verify(token, secret);\nif (!result) throw new Error('Invalid');\n\nconst step = Math.floor(Date.now() / 1000 / 30) + result.delta;\nif (!guard.accept(userId, step)) {\n  throw new Error('Token already used');\n}\n```\n\n#### `isValidTokenFormat(token, options?)`\nPre-validate token shape before calling `verify()`.\n\n```js\nif (!isValidTokenFormat(input)) return res.status(400).send('Bad format');\n```\n\n### Steam Guard\n\n#### `steamTOTP(secret, options?)`\nGenerate a 5-character Steam Guard code.\n\n```js\nconst code = await steamTOTP(secret); // → \"K8R3F\"\n```\n\n---\n\n## 💡 Examples\n\n### Express.js — Enable 2FA for a user\n\n```js\nimport express from 'express';\nimport QRCode from 'qrcode';\nimport { generateSecret, buildURI, verify } from '2fa-lib';\n\nconst app = express();\n\napp.post('/2fa/setup', async (req, res) => {\n  const secret = generateSecret();\n  await db.users.update(req.user.id, { totpSecret: secret, totpEnabled: false });\n\n  const uri = buildURI({\n    secret,\n    label: req.user.email,\n    issuer: 'MyApp',\n  });\n\n  const qrDataUrl = await QRCode.toDataURL(uri);\n  res.json({ qrDataUrl, secret });\n});\n\napp.post('/2fa/verify', async (req, res) => {\n  const user = await db.users.findById(req.user.id);\n  const result = await verify(req.body.code, user.totpSecret, { window: 1 });\n\n  if (!result) return res.status(401).json({ error: 'Invalid code' });\n\n  await db.users.update(user.id, { totpEnabled: true });\n  res.json({ success: true });\n});\n```\n\n### Next.js App Router — Login with 2FA\n\n```ts\n// app/api/login/route.ts\nimport { verify, createRateLimiter } from '2fa-lib';\n\nconst limiter = createRateLimiter({ max: 5, windowMs: 60_000 });\n\nexport async function POST(req: Request) {\n  const { email, password, code } = await req.json();\n\n  if (!limiter.allow(email)) {\n    return Response.json({ error: 'Too many attempts' }, { status: 429 });\n  }\n\n  const user = await db.users.findByEmail(email);\n  // ... validate password ...\n\n  if (user.totpEnabled) {\n    const result = await verify(code, user.totpSecret);\n    if (!result) return Response.json({ error: 'Invalid 2FA code' }, { status: 401 });\n  }\n\n  return Response.json({ token: createSessionToken(user) });\n}\n```\n\n### Cloudflare Workers — Edge runtime\n\n```js\nimport { totp, verify } from '2fa-lib';\n\nexport default {\n  async fetch(request, env) {\n    const code = await totp(env.USER_SECRET);\n    return new Response(code);\n  },\n};\n```\n\n---\n\n## 🤔 Why 2fa-lib?\n\n| Feature | 2fa-lib | otplib | speakeasy | otpauth |\n|---|:---:|:---:|:---:|:---:|\n| Zero dependencies | ✅ | ❌ | ❌ | ✅ |\n| TypeScript types built-in | ✅ | ✅ | ❌ | ✅ |\n| Edge runtime support | ✅ | ⚠️ | ❌ | ✅ |\n| Microsoft Authenticator helper | ✅ | ❌ | ❌ | ❌ |\n| `otpauth://` URI parser | ✅ | ❌ | ❌ | ✅ |\n| Backup code hashing | ✅ | ❌ | ❌ | ❌ |\n| Built-in rate limiter | ✅ | ❌ | ❌ | ❌ |\n| Replay protection | ✅ | ❌ | ❌ | ❌ |\n| Steam Guard | ✅ | ❌ | ❌ | ❌ |\n| Bundle size (min+gzip) | <5 KB | ~15 KB | ~30 KB | ~8 KB |\n\n---\n\n## 🔐 Security Best Practices\n\n1. **Encrypt secrets at rest.** Never store TOTP secrets in plaintext in your database.\n2. **Use the rate limiter.** Brute-forcing 6 digits takes ~1M attempts on average — enforce limits.\n3. **Use the replay guard.** Don't allow the same code to be used twice.\n4. **Hash backup codes.** Never store them in plaintext.\n5. **Use HTTPS.** Always.\n6. **Allow drift.** Use `window: 1` (±30s) to handle clock skew between server and phone.\n\n---\n\n## 🧪 Testing\n\n```bash\nnpm test\n```\n\nThe library is tested against the official **RFC 4226** and **RFC 6238** test vectors.\n\n---\n\n## 📖 Standards\n\n- [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238) — Time-based One-Time Password (TOTP)\n- [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226) — HMAC-based One-Time Password (HOTP)\n- [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648) — Base32 encoding\n- [Key URI Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format) — Google Authenticator\n\n---\n\n## 🤝 Contributing\n\nPRs welcome! Please open an issue first for major changes.\n\n---\n\n## 📄 License\n\n[MIT](./LICENSE) © [ai-mehedi](https://github.com/ai-mehedi)\n\n---\n\n<div align=\"center\">\n\n### Keywords\n\n`totp` · `hotp` · `2fa` · `mfa` · `two-factor authentication` · `multi-factor authentication`\n`google authenticator` · `microsoft authenticator` · `authy` · `1password` · `bitwarden`\n`one-time password` · `rfc 6238` · `rfc 4226` · `otpauth` · `qr code` · `authentication`\n`security` · `nodejs` · `typescript` · `deno` · `bun` · `cloudflare workers` · `edge`\n`zero dependencies` · `lightweight` · `tiny` · `esm` · `web crypto` · `hmac`\n\n⭐ **If you find this useful, please [star the repo on GitHub](https://github.com/ai-mehedi/2fa-lib)!** ⭐\n\n</div>\n","readmeFilename":"README.md"}