diff --git a/src/lib/auth.config.ts b/src/lib/auth.config.ts index 12b819e..8435301 100644 --- a/src/lib/auth.config.ts +++ b/src/lib/auth.config.ts @@ -59,6 +59,9 @@ export const authConfig: NextAuthConfig = { '/reset-password', '/apply', '/lunch/pick', // external attendees pick a dish via signed token (no account) + '/vote', // audience QR voting at the grand finale (token-based, no account) + '/live-scores', // public live scoreboard + '/live/ceremony', // big-screen ceremony view (projector, no account) '/api/auth', '/api/trpc', // tRPC handles its own auth via procedures '/api/cron', // cron endpoints self-guard via x-cron-secret (CRON_SECRET) diff --git a/tests/unit/auth-public-paths.test.ts b/tests/unit/auth-public-paths.test.ts index 195140f..4f1d76b 100644 --- a/tests/unit/auth-public-paths.test.ts +++ b/tests/unit/auth-public-paths.test.ts @@ -22,4 +22,24 @@ describe('middleware public paths', () => { it('blocks a protected page without a session', () => { expect(authorized('/admin/logistics', null)).toBe(false) }) + + // Grand finale: audience QR voting, public scoreboard, and the big-screen + // ceremony view are all reached by attendees with NO account. + it('allows audience voting pages without a session', () => { + expect(authorized('/vote/competition/round123', null)).toBe(true) + expect(authorized('/vote/session456', null)).toBe(true) + }) + + it('allows the public live scoreboard without a session', () => { + expect(authorized('/live-scores/session456', null)).toBe(true) + }) + + it('allows the big-screen ceremony view without a session', () => { + expect(authorized('/live/ceremony/round123', null)).toBe(true) + }) + + it('keeps jury and admin live surfaces private', () => { + expect(authorized('/jury/competitions/round123/live', null)).toBe(false) + expect(authorized('/admin', null)).toBe(false) + }) })