Files
fragginwagon c728016dec feat: establish auth listing and image vertical slices
Includes MFA-capable authentication, session and CSRF security, password recovery, listing lifecycle and ownership rules, inventory image metadata, local ImageStorage, migrations, tests, and implementation status documentation. Verification: npm test (62 passing) and integration suite (14 passing).
2026-09-08 17:32:03 -04:00

91 lines
2.8 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { buildApp } from '../../src/app/build-app.js';
const userId = '464e9b49-8617-4d00-af3c-78b3294e1d70';
const methodId = '6833868a-2a1b-4d33-90d1-fc9f2b7be869';
const headers = {
cookie: 'pokemon_inventory_session=session-token',
'x-csrf-token': 'csrf-token',
};
function createTestApp(mfaService) {
return buildApp({
authenticationService: {
async authorizeUnsafeRequest(sessionToken, csrfToken) {
assert.equal(sessionToken, 'session-token');
assert.equal(csrfToken, 'csrf-token');
return { userId };
},
},
loginRateLimiter: { async consume() { return { allowed: true }; } },
mfaService,
});
}
test('enrolls MFA only for the authenticated user and returns its provisioning URI', async (testContext) => {
const app = createTestApp({
async enroll(input) {
assert.deepEqual(input, { userId, name: 'Authenticator' });
return { id: methodId, provisioningUri: 'otpauth://totp/example' };
},
});
testContext.after(() => app.close());
const response = await app.inject({
method: 'POST',
url: '/api/v1/auth/mfa/enroll',
headers,
payload: { name: 'Authenticator' },
});
assert.equal(response.statusCode, 201);
assert.equal(response.json().data.provisioningUri, 'otpauth://totp/example');
});
test('verifies MFA with CSRF protection and does not expose secret material', async (testContext) => {
const app = createTestApp({
async verifyEnrollment(input) {
assert.deepEqual(input, { userId, methodId, code: '123456' });
return { id: methodId, name: 'Authenticator', type: 'TOTP', secretCiphertext: 'hidden' };
},
});
testContext.after(() => app.close());
const response = await app.inject({
method: 'POST',
url: '/api/v1/auth/mfa/verify',
headers,
payload: { methodId, code: '123456' },
});
assert.equal(response.statusCode, 200);
assert.equal(response.json().data.secretCiphertext, undefined);
assert.equal(response.json().data.type, 'TOTP');
});
test('rejects MFA enrollment without CSRF validation', async (testContext) => {
const app = buildApp({
authenticationService: {
async authorizeUnsafeRequest() {
const error = new Error('CSRF validation failed.');
error.code = 'FORBIDDEN';
error.statusCode = 403;
throw error;
},
},
loginRateLimiter: { async consume() { return { allowed: true }; } },
mfaService: { async enroll() {} },
});
testContext.after(() => app.close());
const response = await app.inject({
method: 'POST',
url: '/api/v1/auth/mfa/enroll',
payload: { name: 'Authenticator' },
});
assert.equal(response.statusCode, 403);
assert.equal(response.json().error.code, 'FORBIDDEN');
});