27 lines
1.5 KiB
JavaScript
27 lines
1.5 KiB
JavaScript
'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);
|
|
});
|