import { createHmac, timingSafeEqual } from 'crypto'; import { redirect, type Handle } from '@sveltejs/kit'; import { PRIVATE_JWT_SECRET } from '$env/static/private'; import { client } from './lib/apiClient.ts'; function verifyJwt(token: string, secret: string): boolean { try { const parts = token.split('.'); if (parts.length !== 3) return false; const [header, payload, sig] = parts; const expected = createHmac('sha256', secret) .update(`${header}.${payload}`) .digest('base64url'); const expectedBuf = Buffer.from(expected); const sigBuf = Buffer.from(sig); if (expectedBuf.length !== sigBuf.length) return false; if (!timingSafeEqual(expectedBuf, sigBuf)) return false; const claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')); if (claims.exp && claims.exp < Date.now() / 1000) return false; return true; } catch { return false; } } export const handle: Handle = async ({ event, resolve }) => { event.locals.apiClient = client; const rawToken = event.cookies.get('auth_token'); const isValid = rawToken ? verifyJwt(rawToken, PRIVATE_JWT_SECRET) : false; event.locals.authToken = isValid ? rawToken! : null; if (event.url.pathname.startsWith('/app') && !isValid) { return redirect(307, '/login'); } return resolve(event); };