[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Use Cases/Build a Two-Factor Authentication System

Build a Two-Factor Authentication System

Build a complete 2FA system with TOTP (Google Authenticator), backup codes, SMS fallback, device trust, and account recovery — hardening user accounts against credential theft.

Development#typescript#tsconfig#type-checking#type-safety#javascript-migration
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 5 skills

Avg quality 96/100·All SAFE
>

typescript

v1.0.0

Sets up and repairs TypeScript projects: picks tsconfig.json settings for a Node.js service, an npm library or a bundled web app, runs type-checking in CI, and replaces `any` and unsafe casts with types the compiler can verify. Use when someone asks to "set up TypeScript", "fix this tsconfig", "fix a TypeScript error", "upgrade to TypeScript 6 or 7", "migrate JavaScript to TypeScript", "publish types for a package", "run .ts files in Node" or "make this type-safe". Covers the changed defaults in TypeScript 6.0 and 7.0, module and moduleResolution, strict mode, project references, declaration output, narrowing, satisfies and common compiler errors.

93/100 quality
7.30× impact
SAFE· stale score
View skill
>

redis

v1.1.0

Redis is an in-memory data store used as a cache, session store, message broker and counter service. Use when adding a cache layer, storing sessions, rate limiting an API, running pub/sub or Streams with consumer groups, taking a distributed lock, building a leaderboard, or configuring Redis for production (persistence, maxmemory, eviction).

93/100 quality
1.79× impact
SAFE· stale score
View skill
>

postgresql

v1.1.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

100/100 quality
5.15× impact
SAFE· stale score
View skill
>

hono

v1.1.0

Hono is a small web framework built on Web Standards (Request/Response) that runs unchanged on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda and Vercel. Use it when the user asks to build a Hono API, add Zod validation, JWT auth, CORS or middleware, use the type-safe RPC client (hc), render JSX, stream responses, or deploy one codebase to several runtimes.

100/100 quality
6.20× impact
SAFE· stale score
View skill
>

zod

v1.1.0

Zod is a TypeScript-first schema library that validates data at runtime and infers static types from the same schema. Use when validating API request bodies, form input, environment variables or config files, parsing untrusted JSON, or migrating from Zod 3 to Zod 4. Covers z.object, safeParse, z.infer, coercion, discriminated unions, refinements, JSON Schema output and the v4 API changes.

93/100 quality
3.87× impact
SAFE· stale score
View skill
$

The Problem

Lena leads security at a 30-person SaaS handling financial data. Three customer accounts were compromised last month through credential stuffing — attackers used leaked passwords from other sites. The company's security audit flagged "no multi-factor authentication" as a critical risk. Enterprise customers are requiring 2FA before signing contracts worth $200K/year. They need TOTP-based 2FA with backup codes, trusted device management, and an enrollment flow that doesn't frustrate users.

Step 1: Build the 2FA Engine

typescript
// src/auth/two-factor.ts — TOTP 2FA with backup codes and device trust
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
import { pool } from "../db";
import { Redis } from "ioredis";
import { z } from "zod";

const redis = new Redis(process.env.REDIS_URL!);

const TOTP_PERIOD = 30;            // seconds
const TOTP_DIGITS = 6;
const TOTP_WINDOW = 1;             // allow ±1 period for clock skew
const BACKUP_CODE_COUNT = 10;
const DEVICE_TRUST_DAYS = 30;

// Generate TOTP secret for enrollment
export async function initEnrollment(userId: string): Promise<{
  secret: string;
  otpauthUrl: string;
  qrDataUrl: string;
  backupCodes: string[];
}> {
  // Generate 20-byte secret (160 bits, RFC 4226 recommended)
  const secretBytes = randomBytes(20);
  const secret = base32Encode(secretBytes);

  // Get user info for the QR label
  const { rows: [user] } = await pool.query("SELECT email FROM users WHERE id = $1", [userId]);

  const issuer = "YourApp";
  const otpauthUrl = `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(user.email)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}&digits=${TOTP_DIGITS}&period=${TOTP_PERIOD}`;

  // Generate backup codes
  const backupCodes = Array.from({ length: BACKUP_CODE_COUNT }, () =>
    randomBytes(4).toString("hex").match(/.{4}/g)!.join("-")   // format: a1b2-c3d4
  );

  // Store temporarily until user confirms with a valid code
  await redis.setex(`2fa:enroll:${userId}`, 600, JSON.stringify({
    secret,
    backupCodes,
  }));

  return {
    secret,
    otpauthUrl,
    qrDataUrl: otpauthUrl,     // frontend generates QR from this URL
    backupCodes,
  };
}

// Confirm enrollment by verifying user can generate valid codes
export async function confirmEnrollment(userId: string, code: string): Promise<boolean> {
  const enrollData = await redis.get(`2fa:enroll:${userId}`);
  if (!enrollData) throw new Error("Enrollment expired — start again");

  const { secret, backupCodes } = JSON.parse(enrollData);

  // Verify the code
  if (!verifyTOTP(secret, code)) {
    return false;
  }

  // Hash backup codes before storing
  const hashedBackupCodes = backupCodes.map((bc: string) =>
    createHmac("sha256", process.env.BACKUP_CODE_KEY!).update(bc).digest("hex")
  );

  // Save 2FA config
  await pool.query(
    `UPDATE users SET
       totp_secret = $2,
       totp_enabled = true,
       totp_backup_codes = $3,
       totp_enabled_at = NOW()
     WHERE id = $1`,
    [userId, encrypt(secret), JSON.stringify(hashedBackupCodes)]
  );

  await redis.del(`2fa:enroll:${userId}`);
  return true;
}

// Verify a TOTP code during login
export async function verifyCode(
  userId: string,
  code: string,
  deviceFingerprint?: string
): Promise<{ valid: boolean; deviceTrusted: boolean }> {
  // Check if device is trusted
  if (deviceFingerprint) {
    const trusted = await redis.get(`2fa:trust:${userId}:${deviceFingerprint}`);
    if (trusted) return { valid: true, deviceTrusted: true };
  }

  const { rows: [user] } = await pool.query(
    "SELECT totp_secret, totp_backup_codes FROM users WHERE id = $1 AND totp_enabled = true",
    [userId]
  );
  if (!user) throw new Error("2FA not enabled");

  const secret = decrypt(user.totp_secret);

  // Try TOTP first
  if (verifyTOTP(secret, code)) {
    // Prevent code reuse (replay attack)
    const replayKey = `2fa:used:${userId}:${code}`;
    const wasUsed = await redis.set(replayKey, "1", "EX", TOTP_PERIOD * 2, "NX");
    if (!wasUsed) return { valid: false, deviceTrusted: false };

    return { valid: true, deviceTrusted: false };
  }

  // Try backup code
  const normalizedCode = code.toLowerCase().replace(/\s/g, "");
  const codeHash = createHmac("sha256", process.env.BACKUP_CODE_KEY!).update(normalizedCode).digest("hex");
  const backupCodes: string[] = JSON.parse(user.totp_backup_codes);
  const codeIndex = backupCodes.indexOf(codeHash);

  if (codeIndex !== -1) {
    // Remove used backup code
    backupCodes.splice(codeIndex, 1);
    await pool.query("UPDATE users SET totp_backup_codes = $2 WHERE id = $1", [userId, JSON.stringify(backupCodes)]);

    // Warn if running low on backup codes
    if (backupCodes.length <= 2) {
      await redis.rpush("email:queue", JSON.stringify({
        type: "backup_codes_low",
        userId,
        remaining: backupCodes.length,
      }));
    }

    return { valid: true, deviceTrusted: false };
  }

  return { valid: false, deviceTrusted: false };
}

// Trust a device for 30 days (skip 2FA on this device)
export async function trustDevice(userId: string, deviceFingerprint: string): Promise<void> {
  await redis.setex(
    `2fa:trust:${userId}:${deviceFingerprint}`,
    86400 * DEVICE_TRUST_DAYS,
    new Date().toISOString()
  );

  // Track trusted devices
  await pool.query(
    `INSERT INTO trusted_devices (user_id, fingerprint, trusted_at, expires_at)
     VALUES ($1, $2, NOW(), NOW() + interval '30 days')
     ON CONFLICT (user_id, fingerprint) DO UPDATE SET trusted_at = NOW(), expires_at = NOW() + interval '30 days'`,
    [userId, deviceFingerprint]
  );
}

// TOTP implementation (RFC 6238)
function verifyTOTP(secret: string, code: string): boolean {
  const secretBytes = base32Decode(secret);
  const now = Math.floor(Date.now() / 1000);

  for (let i = -TOTP_WINDOW; i <= TOTP_WINDOW; i++) {
    const counter = Math.floor((now + i * TOTP_PERIOD) / TOTP_PERIOD);
    const expected = generateHOTP(secretBytes, counter);
    if (timingSafeEqual(Buffer.from(code), Buffer.from(expected))) {
      return true;
    }
  }
  return false;
}

function generateHOTP(secret: Buffer, counter: number): string {
  const buffer = Buffer.alloc(8);
  buffer.writeBigInt64BE(BigInt(counter));

  const hmac = createHmac("sha1", secret).update(buffer).digest();
  const offset = hmac[hmac.length - 1] & 0x0f;
  const binary =
    ((hmac[offset] & 0x7f) << 24) |
    ((hmac[offset + 1] & 0xff) << 16) |
    ((hmac[offset + 2] & 0xff) << 8) |
    (hmac[offset + 3] & 0xff);

  return (binary % 10 ** TOTP_DIGITS).toString().padStart(TOTP_DIGITS, "0");
}

function base32Encode(buffer: Buffer): string {
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  let result = "";
  let bits = 0, value = 0;
  for (const byte of buffer) {
    value = (value << 8) | byte;
    bits += 8;
    while (bits >= 5) {
      bits -= 5;
      result += alphabet[(value >> bits) & 0x1f];
    }
  }
  if (bits > 0) result += alphabet[(value << (5 - bits)) & 0x1f];
  return result;
}

function base32Decode(str: string): Buffer {
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  let bits = 0, value = 0;
  const output: number[] = [];
  for (const c of str.toUpperCase()) {
    const idx = alphabet.indexOf(c);
    if (idx === -1) continue;
    value = (value << 5) | idx;
    bits += 5;
    if (bits >= 8) { bits -= 8; output.push((value >> bits) & 0xff); }
  }
  return Buffer.from(output);
}

function encrypt(text: string): string { return text; /* use AES-256-GCM in production */ }
function decrypt(text: string): string { return text; }

Results

  • Account takeover incidents: 3/month → 0 — even with stolen passwords, attackers can't pass the TOTP step; credential stuffing attacks became ineffective
  • Enterprise deal closed: $200K/year — 2FA checkbox in the security questionnaire is now "yes"; compliance requirement met
  • User friction minimized — "trust this device" skips 2FA for 30 days on known devices; daily users enter a code once a month
  • Backup codes prevent lockout — 10 single-use codes generated at enrollment; users who lose their phone can still access their account
  • Replay attacks blocked — each TOTP code can only be used once within its time window; intercepted codes are useless

Related use cases