import express from 'express'
import crypto from 'node:crypto'
const app = express()
const secret = process.env.CASEDEV_WEBHOOK_SECRET!
app.post('/webhooks/casedev', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body.toString('utf8')
if (!verifySignature(rawBody, req.headers, secret)) {
return res.status(400).send('invalid signature')
}
const event = JSON.parse(rawBody)
console.log(event.type, event.id)
return res.sendStatus(204)
})
function verifySignature(body: string, headers: express.Request['headers'], secret: string) {
const id = String(headers['case-webhook-id'] ?? '')
const timestamp = String(headers['case-webhook-timestamp'] ?? '')
const signature = String(headers['case-webhook-signature'] ?? '')
const timestampSeconds = Number(timestamp)
if (!id || !Number.isFinite(timestampSeconds)) return false
if (Math.abs(Date.now() / 1000 - timestampSeconds) > 300) return false
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64')
const expected = crypto
.createHmac('sha256', key)
.update(`${id}.${timestamp}.${body}`)
.digest('base64')
return signature.split(' ').some((part) => {
const [version, value] = part.split(',', 2)
return version === 'v1' && safeEqual(value, expected)
})
}
function safeEqual(a = '', b = '') {
const left = Buffer.from(a)
const right = Buffer.from(b)
return left.length === right.length && crypto.timingSafeEqual(left, right)
}