import crypto from 'crypto' export function verifyHMAC( rawBody: Buffer, timestamp: string, signature: string, clientSecret: string ): boolean { const now = Math.floor(Date.now() / 1000) const ts = parseInt(timestamp, 10) if (isNaN(ts) || Math.abs(now - ts) > 300) return false const msg = Buffer.concat([Buffer.from(timestamp), Buffer.from('.'), rawBody]) const expected = crypto.createHmac('sha256', clientSecret).update(msg).digest('hex') try { return crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex')) } catch { return false } } export function checkAuth( rawBody: Buffer, headers: Headers ): { ok: boolean; error?: string } { const clientId = process.env.REPORTING_CLIENT_ID const clientSecret = process.env.REPORTING_CLIENT_SECRET // If credentials not configured, skip auth (dev mode) if (!clientId || !clientSecret) return { ok: true } const reqClientId = headers.get('x-client-id') const timestamp = headers.get('x-timestamp') const signature = headers.get('x-signature') if (!reqClientId || !timestamp || !signature) { return { ok: false, error: 'Missing auth headers' } } if (reqClientId !== clientId) { return { ok: false, error: 'Invalid client ID' } } if (!verifyHMAC(rawBody, timestamp, signature, clientSecret)) { return { ok: false, error: 'Invalid signature' } } return { ok: true } }