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);
}