'use strict'; const assert = require('node:assert/strict'); const test = require('node:test'); const { createTotpService, base32Decode, base32Encode, generateTotp } = require('../../src/security/totp'); test('TOTP uses RFC-compatible base32 and time steps', () => { assert.equal(base32Encode(Buffer.from('foo')), 'MZXW6'); assert.equal(base32Decode('MZXW6').toString(), 'foo'); assert.equal(generateTotp('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ', 59_000, { digits: 8 }), '94287082'); }); test('TOTP enrollment is opt-in and recovery codes are one-use', () => { const values = new Map(); const secretValues = new Map(); const now = 1_700_000_000_000; const settings = { get: (key, fallback) => values.has(key) ? values.get(key) : fallback, set: (key, value) => values.set(key, value) }; const secrets = { has: key => secretValues.has(key), get: key => secretValues.get(key) || '', set: (key, value) => value ? secretValues.set(key, value) : secretValues.delete(key) }; const service = createTotpService({ settings, secrets, now: () => now }); assert.equal(service.status().enabled, false); const enrollment = service.begin({ account: 'Jens' }); const confirmed = service.confirm({ code: generateTotp(enrollment.secret, now) }); assert.equal(confirmed.enabled, true); assert.equal(confirmed.recoveryCodes.length, 8); assert.equal(service.verifyLoginCode(confirmed.recoveryCodes[0]), true); assert.equal(service.verifyLoginCode(confirmed.recoveryCodes[0]), false); assert.equal(service.status().recoveryCodesRemaining, 7); });