/** * Tests for server/middleware/requireAuth.js * * requireAuth guards all authenticated API routes. Tests exercise the full * range of valid/invalid cookie states to ensure there's no bypass path. */ import requireAuth from '../../server/middleware/requireAuth.js'; // Build mock req/res/next objects function makeReq({ signedCookie, plainCookie, cookieSecret } = {}) { // Set COOKIE_SECRET so signed path is taken when provided if (cookieSecret !== undefined) { process.env.COOKIE_SECRET = cookieSecret; } else { delete process.env.COOKIE_SECRET; } return { signedCookies: { emby_user: signedCookie }, cookies: { emby_user: plainCookie } }; } function makeRes() { const res = { statusCode: null, body: null, status(code) { this.statusCode = code; return this; }, json(body) { this.body = body; return this; } }; return res; } afterEach(() => { delete process.env.COOKIE_SECRET; }); describe('requireAuth middleware', () => { describe('valid sessions', () => { it('calls next() with a valid signed cookie', () => { const payload = JSON.stringify({ id: 'u1', name: 'Alice', isAdmin: true }); const req = makeReq({ signedCookie: payload, cookieSecret: 'secret' }); const res = makeRes(); const next = vi.fn(); requireAuth(req, res, next); expect(next).toHaveBeenCalledOnce(); expect(req.user).toMatchObject({ id: 'u1', name: 'Alice', isAdmin: true }); }); it('calls next() with a valid unsigned cookie (no COOKIE_SECRET)', () => { const payload = JSON.stringify({ id: 'u2', name: 'Bob', isAdmin: false }); const req = makeReq({ plainCookie: payload }); const res = makeRes(); const next = vi.fn(); requireAuth(req, res, next); expect(next).toHaveBeenCalledOnce(); expect(req.user.id).toBe('u2'); }); it('coerces non-boolean isAdmin to boolean', () => { const payload = JSON.stringify({ id: 'u3', name: 'Charlie', isAdmin: 1 }); const req = makeReq({ plainCookie: payload }); const res = makeRes(); const next = vi.fn(); requireAuth(req, res, next); expect(next).toHaveBeenCalled(); expect(req.user.isAdmin).toBe(true); }); }); describe('missing or invalid cookies', () => { it('returns 401 when no cookie is present', () => { const req = makeReq({}); const res = makeRes(); const next = vi.fn(); requireAuth(req, res, next); expect(res.statusCode).toBe(401); expect(next).not.toHaveBeenCalled(); }); it('returns 401 when signed cookie value is false (tampered)', () => { // cookie-parser sets signed cookie to false when signature is invalid const req = makeReq({ signedCookie: false, cookieSecret: 'secret' }); const res = makeRes(); const next = vi.fn(); requireAuth(req, res, next); expect(res.statusCode).toBe(401); }); it('returns 401 for malformed JSON in cookie', () => { const req = makeReq({ plainCookie: 'not-json' }); const res = makeRes(); const next = vi.fn(); requireAuth(req, res, next); expect(res.statusCode).toBe(401); expect(res.body.error).toBe('Invalid session'); }); it('returns 401 when id is missing', () => { const payload = JSON.stringify({ name: 'Alice', isAdmin: false }); const req = makeReq({ plainCookie: payload }); requireAuth(req, makeRes(), vi.fn()); // no next called — handled in the assertion below const res = makeRes(); const next = vi.fn(); requireAuth(req, res, next); expect(res.statusCode).toBe(401); expect(next).not.toHaveBeenCalled(); }); it('returns 401 when name is missing', () => { const payload = JSON.stringify({ id: 'u1', isAdmin: false }); const req = makeReq({ plainCookie: payload }); const res = makeRes(); requireAuth(req, res, vi.fn()); expect(res.statusCode).toBe(401); }); it('returns 401 when id is empty string', () => { const payload = JSON.stringify({ id: '', name: 'Alice', isAdmin: false }); const req = makeReq({ plainCookie: payload }); const res = makeRes(); requireAuth(req, res, vi.fn()); expect(res.statusCode).toBe(401); }); }); });