File size: 1,422 Bytes
020ed68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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 }
}