← All tasks
javascriptclaude-code/javascript-t1 #14Not a task: already works

TOTP Generator (javascript, written by Claude Code)

envgap__claude-code__javascript-t1-14

Written by a coding agent; not on GitHubWritten 2026-02-27

01 / FAILURE SIGNATURE

As the study recorded it

No identifying execution failure has been captured.
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
package.json
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/javascript-t1 #14 · read the task the agent was given
Claude Code wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: TOTP Generator

Write a program that generates and validates Time-based One-Time Passwords (TOTP) compatible with RFC 6238, supporting secret key management, QR code URI generation, and multi-account storage.

FUNCTIONAL REQUIREMENTS:
- Support subcommands: generate (create a new TOTP secret), code (show current OTP code), verify (check if a code is valid), and list (show all stored accounts)
- generate: Create a cryptographically random base32-encoded secret key of configurable length (--length flag, default 20 bytes)
- code: Display the current 6-digit TOTP code for a given account, with a countdown timer showing seconds until the code expires (30-second default period)
- verify: Accept a 6-digit code and check if it matches the current or adjacent time windows (configurable drift tolerance via --drift flag, default 1 window)
- Support configurable TOTP parameters: digit count (6 or 8), time period (30 or 60 seconds), and hash algorithm (SHA-1, SHA-256, SHA-512)
- Generate otpauth:// URIs compatible with authenticator apps (Google Authenticator, Authy)
- Store account secrets in an encrypted local JSON file using a master password
- Support multiple accounts with labels (--account flag with issuer:username format)
- Print the current code, remaining seconds, and next code to console
- If no arguments are given, generate a demo account with a random secret, display the current code and the otpauth:// URI, verify the current code, then show what happens with an incorrect code
- Handle errors: invalid base32 secrets, expired codes, duplicate account names, wrong master password

Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include:
- Source code
- package.json with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

package.json
{
  "name": "totp-generator",
  "version": "1.0.0",
  "description": "RFC 6238 TOTP code generator and validator with multi-account storage",
  "main": "totp.js",
  "scripts": {
    "start": "node totp.js"
  },
  "keywords": ["totp", "otp", "2fa", "authenticator", "rfc6238"],
  "license": "MIT",
  "dependencies": {
    "otplib": "12.0.1",
    "qrcode": "1.5.3"
  }
}
README.md
# TOTP Generator - JavaScript (Trial 1)

A TOTP (Time-based One-Time Password) generator and validator implementing RFC 6238, with multi-account storage, otpauth:// URI generation, and QR code output.

## Dependencies

- **otplib** (12.0.1) - One-time password library for TOTP/HOTP generation and verification
- **qrcode** (1.5.3) - QR code generation for otpauth URIs

## Setup

```bash
npm install
```

## Usage

Run the interactive CLI:

```bash
npm start
```

### Features

- Add TOTP accounts with custom or auto-generated secrets
- Generate current TOTP codes for any stored account
- Validate TOTP codes with configurable window tolerance
- Generate otpauth:// URIs for authenticator apps
- Generate QR code PNG images
- Multi-account JSON file storage
- List and remove stored accounts

### Programmatic Usage

```javascript
const { addAccount, generateTotp, validateTotp, getOtpauthUri } = require("./totp");

addAccount("user@example.com", "GitHub");
const code = generateTotp("GitHub:user@example.com");
const isValid = validateTotp("GitHub:user@example.com", code);
const uri = getOtpauthUri("GitHub:user@example.com");
```
totp.js
/**
 * TOTP Generator - Generates and validates RFC 6238 TOTP codes with
 * multi-account storage and otpauth:// URI generation.
 *
 * Dependencies: otplib, qrcode
 */

const fs = require("fs");
const path = require("path");
const readline = require("readline");
const { authenticator } = require("otplib");
const QRCode = require("qrcode");

const ACCOUNTS_FILE = path.join(__dirname, "totp_accounts.json");

/**
 * Load accounts from the JSON storage file.
 * @returns {Object} Map of account key to account data.
 */
function loadAccounts() {
  if (fs.existsSync(ACCOUNTS_FILE)) {
    const data = fs.readFileSync(ACCOUNTS_FILE, "utf-8");
    return JSON.parse(data);
  }
  return {};
}

/**
 * Save accounts to the JSON storage file.
 * @param {Object} accounts - Map of account key to account data.
 */
function saveAccounts(accounts) {
  fs.writeFileSync(ACCOUNTS_FILE, JSON.stringify(accounts, null, 2), "utf-8");
}

/**
 * Add a new TOTP account.
 * @param {string} name - Account name / email.
 * @param {string} issuer - Service name.
 * @param {string|null} secret - Base32 secret (auto-generated if null).
 * @param {number} digits - Number of digits (default 6).
 * @param {number} period - Time step in seconds (default 30).
 * @returns {Object} The stored account object.
 */
function addAccount(name, issuer = "", secret = null, digits = 6, period = 30) {
  const accounts = loadAccounts();

  if (!secret) {
    secret = authenticator.generateSecret();
  }

  const account = { name, issuer, secret, digits, period };
  const key = issuer ? `${issuer}:${name}` : name;
  accounts[key] = account;
  saveAccounts(accounts);
  console.log(`Account '${key}' added successfully.`);
  return account;
}

/**
 * Generate the current TOTP code for a given account.
 * @param {string} accountKey - Account identifier.
 * @returns {string} The TOTP code.
 */
function generateTotp(accountKey) {
  const accounts = loadAccounts();
  if (!accounts[accountKey]) {
    throw new Error(`Account '${accountKey}' not found.`);
  }

  const acct = accounts[accountKey];

  // Configure authenticator options
  authenticator.options = {
    digits: acct.digits || 6,
    step: acct.period || 30,
  };

  const code = authenticator.generate(acct.secret);
  const remaining =
    (acct.period || 30) - (Math.floor(Date.now() / 1000) % (acct.period || 30));
  console.log(`TOTP for '${accountKey}': ${code}  (valid for ${remaining}s)`);

  // Reset options
  authenticator.resetOptions();
  return code;
}

/**
 * Validate a TOTP code against a stored account.
 * @param {string} accountKey - Account identifier.
 * @param {string} code - The TOTP code to validate.
 * @param {number} window - Time-step window for validation (default 1).
 * @returns {boolean} True if valid.
 */
function validateTotp(accountKey, code, window = 1) {
  const accounts = loadAccounts();
  if (!accounts[accountKey]) {
    throw new Error(`Account '${accountKey}' not found.`);
  }

  const acct = accounts[accountKey];

  authenticator.options = {
    digits: acct.digits || 6,
    step: acct.period || 30,
    window: window,
  };

  const valid = authenticator.check(code, acct.secret);
  const status = valid ? "VALID" : "INVALID";
  console.log(`Code '${code}' for '${accountKey}' is ${status}.`);

  authenticator.resetOptions();
  return valid;
}

/**
 * Generate an otpauth:// URI for the given account.
 * @param {string} accountKey - Account identifier.
 * @returns {string} The otpauth URI.
 */
function getOtpauthUri(accountKey) {
  const accounts = loadAccounts();
  if (!accounts[accountKey]) {
    throw new Error(`Account '${accountKey}' not found.`);
  }

  const acct = accounts[accountKey];
  const uri = authenticator.keyuri(acct.name, acct.issuer || "", acct.secret);
  console.log(`otpauth URI: ${uri}`);
  return uri;
}

/**
 * Generate a QR code image file for the account's otpauth URI.
 * @param {string} accountKey - Account identifier.
 * @param {string|null} outputFile - Output file path (auto-generated if null).
 * @returns {Promise<string>} Path to the QR code image.
 */
async function generateQrCode(accountKey, outputFile = null) {
  const uri = getOtpauthUri(accountKey);
  if (!outputFile) {
    const safeName = accountKey.replace(/:/g, "_").replace(/ /g, "_");
    outputFile = path.join(__dirname, `${safeName}_qr.png`);
  }

  await QRCode.toFile(outputFile, uri, {
    type: "png",
    width: 300,
    margin: 2,
  });

  console.log(`QR code saved to '${outputFile}'.`);
  return outputFile;
}

/**
 * List all stored accounts.
 */
function listAccounts() {
  const accounts = loadAccounts();
  const keys = Object.keys(accounts);
  if (keys.length === 0) {
    console.log("No accounts stored.");
    return [];
  }

  console.log(
    `\n${"Account Key".padEnd(35)} ${"Issuer".padEnd(20)} ${"Digits".padEnd(8)} ${"Period".padEnd(10)}`
  );
  console.log("-".repeat(75));
  for (const key of keys) {
    const a = accounts[key];
    console.log(
      `${key.padEnd(35)} ${(a.issuer || "").padEnd(20)} ${String(
        a.digits || 6
      ).padEnd(8)} ${String(a.period || 30).padEnd(10)}`
    );
  }
  console.log();
  return keys;
}

/**
 * Remove an account from storage.
 * @param {string} accountKey - Account identifier.
 * @returns {boolean} True if removed.
 */
function removeAccount(accountKey) {
  const accounts = loadAccounts();
  if (!accounts[accountKey]) {
    console.log(`Account '${accountKey}' not found.`);
    return false;
  }
  delete accounts[accountKey];
  saveAccounts(accounts);
  console.log(`Account '${accountKey}' removed.`);
  return true;
}

/**
 * Interactive CLI menu.
 */
async function main() {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });

  const ask = (q) => new Promise((resolve) => rl.question(q, resolve));

  while (true) {
    console.log("\n=== TOTP Generator ===");
    console.log("1. Add account");
    console.log("2. Generate TOTP code");
    console.log("3. Validate TOTP code");
    console.log("4. Show otpauth URI");
    console.log("5. Generate QR code");
    console.log("6. List accounts");
    console.log("7. Remove account");
    console.log("8. Exit");

    const choice = (await ask("\nSelect option: ")).trim();

    try {
      switch (choice) {
        case "1": {
          const name = (await ask("Account name: ")).trim();
          const issuer = (await ask("Issuer: ")).trim();
          const secretInput = (await ask("Secret (blank to auto-generate): ")).trim();
          const digitsInput = (await ask("Digits (default 6): ")).trim();
          const periodInput = (await ask("Period in seconds (default 30): ")).trim();
          addAccount(
            name,
            issuer,
            secretInput || null,
            digitsInput ? parseInt(digitsInput) : 6,
            periodInput ? parseInt(periodInput) : 30
          );
          break;
        }
        case "2": {
          const key = (await ask("Account key: ")).trim();
          generateTotp(key);
          break;
        }
        case "3": {
          const vKey = (await ask("Account key: ")).trim();
          const code = (await ask("TOTP code: ")).trim();
          validateTotp(vKey, code);
          break;
        }
        case "4": {
          const uKey = (await ask("Account key: ")).trim();
          getOtpauthUri(uKey);
          break;
        }
        case "5": {
          const qKey = (await ask("Account key: ")).trim();
          await generateQrCode(qKey);
          break;
        }
        case "6":
          listAccounts();
          break;
        case "7": {
          const rKey = (await ask("Account key: ")).trim();
          removeAccount(rKey);
          break;
        }
        case "8":
          console.log("Goodbye.");
          rl.close();
          return;
        default:
          console.log("Invalid option.");
      }
    } catch (err) {
      console.error("Error:", err.message);
    }
  }
}

// Export functions for programmatic use
module.exports = {
  addAccount,
  generateTotp,
  validateTotp,
  getOtpauthUri,
  generateQrCode,
  listAccounts,
  removeAccount,
};

// Run CLI if executed directly
if (require.main === module) {
  main().catch(console.error);
}