From 0753cfaa56689339b26ac22af0fc9b01d2169efb Mon Sep 17 00:00:00 2001 From: NeilClack Date: Mon, 17 Aug 2026 21:08:50 -0500 Subject: [PATCH] Remove v2API key --- src/hooks.server.spec.ts | 155 +++++++++++++++++++++------------------ src/hooks.server.ts | 53 ++++++------- 2 files changed, 110 insertions(+), 98 deletions(-) diff --git a/src/hooks.server.spec.ts b/src/hooks.server.spec.ts index c7dde5c..c093c32 100644 --- a/src/hooks.server.spec.ts +++ b/src/hooks.server.spec.ts @@ -129,79 +129,90 @@ describe('hooks.server handle', () => { ); }); - describe('v2 auth gate', () => { - it('returns 401 for v2 routes without an Authorization header', async () => { - const event = createMockEvent('/api/v2/proclamations'); - const res = await runHandle(event); + it('does not apply API key gate to non-API routes', async () => { + getSession.mockResolvedValue(null); - expect(res.status).toBe(401); - expect(await res.json()).toEqual({ error: 'API key required' }); - expect(validateApiKeyMock).not.toHaveBeenCalled(); - expect(event.resolve).not.toHaveBeenCalled(); - }); + const event = createMockEvent('/dashboard'); + await runHandle(event); - it('returns 401 for v2 routes with an invalid bearer key', async () => { - validateApiKeyMock.mockResolvedValue(null); - - const event = createMockEvent('/api/v2/proclamations'); - event.request = new Request('http://localhost/api/v2/proclamations', { - headers: { authorization: 'Bearer sk_invalid' } - }); - const res = await runHandle(event); - - expect(res.status).toBe(401); - expect(await res.json()).toEqual({ error: 'Invalid or expired API key' }); - expect(validateApiKeyMock).toHaveBeenCalledWith('sk_invalid'); - expect(event.resolve).not.toHaveBeenCalled(); - }); - - it('resolves v2 routes with a valid bearer key and sets locals.apiKey', async () => { - validateApiKeyMock.mockResolvedValue({ id: 'key-1', keyPrefix: 'sk_abc123' }); - - const event = createMockEvent('/api/v2/proclamations'); - event.request = new Request('http://localhost/api/v2/proclamations', { - headers: { authorization: 'Bearer sk_abc123def' } - }); - await runHandle(event); - - expect(event.resolve).toHaveBeenCalled(); - expect(event.locals.apiKey).toEqual({ id: 'key-1', keyPrefix: 'sk_abc123' }); - expect(validateApiKeyMock).toHaveBeenCalledWith('sk_abc123def'); - }); - - it('always validates the v2 API key even when API_KEYS_ACTIVE would bypass v1', async () => { - envMock.API_KEYS_ACTIVE = 'false'; - validateApiKeyMock.mockResolvedValue(null); - - const event = createMockEvent('/api/v2/frequencies'); - event.request = new Request('http://localhost/api/v2/frequencies', { - headers: { authorization: 'Bearer sk_abc' } - }); - const res = await runHandle(event); - - expect(res.status).toBe(401); - expect(await res.json()).toEqual({ error: 'Invalid or expired API key' }); - expect(validateApiKeyMock).toHaveBeenCalledWith('sk_abc'); - }); - - it('does not require an API key for signed v2 onboarding routes', async () => { - getSession.mockResolvedValue(null); - - const event = createMockEvent('/api/v2/automations/create/account'); - await runHandle(event); - - expect(validateApiKeyMock).not.toHaveBeenCalled(); - expect(svelteKitHandler).toHaveBeenCalled(); - }); - - it('does not apply the v2 gate to non-API routes', async () => { - getSession.mockResolvedValue(null); - - const event = createMockEvent('/dashboard'); - await runHandle(event); - - expect(validateApiKeyMock).not.toHaveBeenCalled(); - expect(svelteKitHandler).toHaveBeenCalled(); - }); + expect(validateApiKeyMock).not.toHaveBeenCalled(); + expect(svelteKitHandler).toHaveBeenCalled(); }); + + // TODO: re-enable when v2 API key auth is ready. + // describe('v2 auth gate', () => { + // it('returns 401 for v2 routes without an Authorization header', async () => { + // const event = createMockEvent('/api/v2/proclamations'); + // const res = await runHandle(event); + // + // expect(res.status).toBe(401); + // expect(await res.json()).toEqual({ error: 'API key required' }); + // expect(validateApiKeyMock).not.toHaveBeenCalled(); + // expect(event.resolve).not.toHaveBeenCalled(); + // }); + // + // it('returns 401 for v2 routes with an invalid bearer key', async () => { + // validateApiKeyMock.mockResolvedValue(null); + // + // const event = createMockEvent('/api/v2/proclamations'); + // event.request = new Request('http://localhost/api/v2/proclamations', { + // headers: { authorization: 'Bearer sk_invalid' } + // }); + // const res = await runHandle(event); + // + // expect(res.status).toBe(401); + // expect(await res.json()).toEqual({ error: 'Invalid or expired API key' }); + // expect(validateApiKeyMock).toHaveBeenCalledWith('sk_invalid'); + // expect(event.resolve).not.toHaveBeenCalled(); + // }); + // + // it('resolves v2 routes with a valid bearer key and sets locals.apiKey', async () => { + // validateApiKeyMock.mockResolvedValue({ id: 'key-1', keyPrefix: 'sk_abc123' }); + // + // const event = createMockEvent('/api/v2/proclamations'); + // event.request = new Request('http://localhost/api/v2/proclamations', { + // headers: { authorization: 'Bearer sk_abc123def' } + // }); + // await runHandle(event); + // + // expect(event.resolve).toHaveBeenCalled(); + // expect(event.locals.apiKey).toEqual({ id: 'key-1', keyPrefix: 'sk_abc123' }); + // expect(validateApiKeyMock).toHaveBeenCalledWith('sk_abc123def'); + // }); + // + // it('always validates the v2 API key even when API_KEYS_ACTIVE would bypass v1', async () => { + // envMock.API_KEYS_ACTIVE = 'false'; + // validateApiKeyMock.mockResolvedValue(null); + // + // const event = createMockEvent('/api/v2/frequencies'); + // event.request = new Request('http://localhost/api/v2/frequencies', { + // headers: { authorization: 'Bearer sk_abc' } + // }); + // const res = await runHandle(event); + // + // expect(res.status).toBe(401); + // expect(await res.json()).toEqual({ error: 'Invalid or expired API key' }); + // expect(validateApiKeyMock).toHaveBeenCalledWith('sk_abc'); + // }); + // + // it('does not require an API key for signed v2 onboarding routes', async () => { + // getSession.mockResolvedValue(null); + // + // const event = createMockEvent('/api/v2/automations/create/account'); + // await runHandle(event); + // + // expect(validateApiKeyMock).not.toHaveBeenCalled(); + // expect(svelteKitHandler).toHaveBeenCalled(); + // }); + // + // it('does not apply the v2 gate to non-API routes', async () => { + // getSession.mockResolvedValue(null); + // + // const event = createMockEvent('/dashboard'); + // await runHandle(event); + // + // expect(validateApiKeyMock).not.toHaveBeenCalled(); + // expect(svelteKitHandler).toHaveBeenCalled(); + // }); + // }); }); diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 369409f..2585f0a 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,37 +1,38 @@ import { auth } from '$lib/auth'; import { svelteKitHandler } from 'better-auth/svelte-kit'; import { building } from '$app/environment'; -import { validateApiKey } from '$lib/server/api-key'; import { env } from '$env/dynamic/private'; +import { validateApiKey } from '$lib/server/api-key'; export async function handle({ event, resolve }) { // v2 auth is ALWAYS enforced — no API_KEYS_ACTIVE bypass. The only // exceptions are the signed onboarding routes (HMAC-protected). - const isV2 = event.url.pathname.startsWith('/api/v2/'); - const isSignedOnboarding = event.url.pathname.startsWith('/api/v2/automations/'); - - if (isV2 && !isSignedOnboarding) { - const authHeader = event.request.headers.get('authorization'); - if (!authHeader?.startsWith('Bearer ')) { - return new Response(JSON.stringify({ error: 'API key required' }), { - status: 401, - headers: { 'content-type': 'application/json' } - }); - } - - const key = authHeader.slice(7); - const record = await validateApiKey(key); - if (!record) { - return new Response(JSON.stringify({ error: 'Invalid or expired API key' }), { - status: 401, - headers: { 'content-type': 'application/json' } - }); - } - - event.locals.apiKey = record; - - return resolve(event); - } + // TODO: re-enable when v2 API key auth is ready. + // const isV2 = event.url.pathname.startsWith('/api/v2/'); + // const isSignedOnboarding = event.url.pathname.startsWith('/api/v2/automations/'); + // + // if (isV2 && !isSignedOnboarding) { + // const authHeader = event.request.headers.get('authorization'); + // if (!authHeader?.startsWith('Bearer ')) { + // return new Response(JSON.stringify({ error: 'API key required' }), { + // status: 401, + // headers: { 'content-type': 'application/json' } + // }); + // } + // + // const key = authHeader.slice(7); + // const record = await validateApiKey(key); + // if (!record) { + // return new Response(JSON.stringify({ error: 'Invalid or expired API key' }), { + // status: 401, + // headers: { 'content-type': 'application/json' } + // }); + // } + // + // event.locals.apiKey = record; + // + // return resolve(event); + // } if ( event.url.pathname.startsWith('/api/v1/') &&