'use strict'; const { test } = require('node:test'); const assert = require('node:assert'); const config = require('../../config'); const auth = require('../../lib/auth'); function withPasswords(upload, admin, fn) { const before = { uploadPass: config.uploadPass, adminPass: config.adminPass, tokenSecret: config.tokenSecret }; Object.assign(config, { uploadPass: upload, adminPass: admin, tokenSecret: '' }); try { return fn(); } finally { Object.assign(config, before); } } test('issued token verifies until it expires', () => withPasswords('up', 'ad', () => { const now = 1_000_000; const { token, exp } = auth.issue({}, 60, 'session', now); assert.strictEqual(exp, now + 60_000); assert.ok(auth.verify(token, 'session', now + 59_000)); assert.strictEqual(auth.verify(token, 'session', now + 60_000), null); })); test('tampered token is rejected', () => withPasswords('up', 'ad', () => { const { token } = auth.issue({}, 60); const [payload, sig] = token.split('.'); const forged = Buffer.from(JSON.stringify({ exp: Date.now() + 1e12 })).toString('base64url'); assert.strictEqual(auth.verify(`${ forged }.${ sig }`), null); assert.strictEqual(auth.verify(`${ payload }.${ sig.slice(0, -2) }xx`), null); assert.strictEqual(auth.verify('garbage'), null); assert.strictEqual(auth.verify(''), null); assert.strictEqual(auth.verify(undefined), null); })); test('a token is only valid for its purpose', () => withPasswords('up', 'ad', () => { const { token } = auth.issue({ sid: 'x' }, 60, 'admin-download'); assert.strictEqual(auth.verify(token, 'session'), null); assert.strictEqual(auth.verify(token, 'admin-download').sid, 'x'); })); test('changing the upload or the admin password invalidates tokens', () => { const { token } = withPasswords('up', 'ad', () => auth.issue({}, 60)); assert.ok(withPasswords('up', 'ad', () => auth.verify(token))); assert.strictEqual(withPasswords('other', 'ad', () => auth.verify(token)), null); assert.strictEqual(withPasswords('up', 'other', () => auth.verify(token)), null); // works without an admin password too const { token: t2 } = withPasswords('up', false, () => auth.issue({}, 60)); assert.ok(withPasswords('up', false, () => auth.verify(t2))); }); test('an explicit token secret survives password changes', () => { const issue = pass => { config.tokenSecret = 'fixed-secret'; return withPasswords(pass, 'ad', () => { config.tokenSecret = 'fixed-secret'; return auth.issue({}, 60); }); }; const { token } = issue('up'); config.tokenSecret = 'fixed-secret'; const before = { uploadPass: config.uploadPass }; config.uploadPass = 'changed'; try { assert.ok(auth.verify(token)); } finally { config.tokenSecret = ''; config.uploadPass = before.uploadPass; } }); test('safeEqual compares secrets', () => { assert.strictEqual(auth.safeEqual('abc', 'abc'), true); assert.strictEqual(auth.safeEqual('abc', 'abd'), false); assert.strictEqual(auth.safeEqual('abc', 'abcd'), false); assert.strictEqual(auth.safeEqual(undefined, 'abc'), false); assert.strictEqual(auth.safeEqual(undefined, undefined), true); });