{"version":3,"sources":["../../src/webhook/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'crypto';\nimport { Status } from '../error/status';\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nfunction verifyTimestamp(webhookTimestamp: string) {\n  const now = Math.floor(Date.now() / 1000);\n  const timestamp = parseInt(webhookTimestamp, 10);\n  if (isNaN(timestamp)) {\n    throw Status.invalidArgument('invalid webhook timestamp').error();\n  }\n  if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n    throw Status.invalidArgument('webhook timestamp is too old').error();\n  }\n  if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n    throw Status.invalidArgument('webhook timestamp is too new').error();\n  }\n  return timestamp;\n}\n\n/**\n * reference: https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/javascript\n * hono usage:\n * ```ts\n * const webhook = verifyStandardWebhook(c.req.header(), await c.req.text(), 'secret');\n * ```\n */\nexport function verifyStandardWebhook<T = unknown>(\n  headers: Record<string, string>,\n  payload: string,\n  secret: string\n): T {\n  const webhookId = headers['webhook-id'];\n  const webhookTimestamp = headers['webhook-timestamp'];\n  const webhookSignature = headers['webhook-signature'];\n  if (!webhookId || !webhookTimestamp || !webhookSignature) {\n    throw Status.invalidArgument('invalid webhook').error();\n  }\n  const timestamp = verifyTimestamp(webhookTimestamp);\n\n  const encoder = new TextEncoder();\n  const toSign = encoder.encode(`${webhookId}.${timestamp}.${payload}`);\n  const hmac = createHmac('sha256', Buffer.from(secret, 'base64'));\n  const digest = hmac.update(toSign).digest();\n\n  const computedSignature = `v1,${Buffer.from(digest).toString('base64')}`;\n  const expectedSignature = computedSignature.split(',')[1];\n  const passedSignatures = webhookSignature.split(' ');\n\n  for (const versionedSignature of passedSignatures) {\n    const [version, signature] = versionedSignature.split(',');\n    if (version !== 'v1') continue;\n    if (timingSafeEqual(encoder.encode(signature), encoder.encode(expectedSignature))) {\n      try {\n        return JSON.parse(payload) as T;\n      } catch (_) {\n        console.error('invalid payload', payload);\n        throw Status.invalidArgument('invalid webhook payload').error();\n      }\n    }\n  }\n  console.error('webhook verification failed');\n  throw Status.invalidArgument('invalid webhook signature').error();\n}\n"],"mappings":";AAAA,SAAS,YAAY,uBAAuB;AAC5C,SAAS,cAAc;AAEvB,IAAM,+BAA+B,IAAI;AAEzC,SAAS,gBAAgB,kBAA0B;AACjD,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,YAAY,SAAS,kBAAkB,EAAE;AAC/C,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,OAAO,gBAAgB,2BAA2B,EAAE,MAAM;AAAA,EAClE;AACA,MAAI,YAAY,MAAM,8BAA8B;AAClD,UAAM,OAAO,gBAAgB,8BAA8B,EAAE,MAAM;AAAA,EACrE;AACA,MAAI,YAAY,MAAM,8BAA8B;AAClD,UAAM,OAAO,gBAAgB,8BAA8B,EAAE,MAAM;AAAA,EACrE;AACA,SAAO;AACT;AASO,SAAS,sBACd,SACA,SACA,QACG;AACH,QAAM,YAAY,QAAQ,YAAY;AACtC,QAAM,mBAAmB,QAAQ,mBAAmB;AACpD,QAAM,mBAAmB,QAAQ,mBAAmB;AACpD,MAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,kBAAkB;AACxD,UAAM,OAAO,gBAAgB,iBAAiB,EAAE,MAAM;AAAA,EACxD;AACA,QAAM,YAAY,gBAAgB,gBAAgB;AAElD,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,SAAS,QAAQ,OAAO,GAAG,SAAS,IAAI,SAAS,IAAI,OAAO,EAAE;AACpE,QAAM,OAAO,WAAW,UAAU,OAAO,KAAK,QAAQ,QAAQ,CAAC;AAC/D,QAAM,SAAS,KAAK,OAAO,MAAM,EAAE,OAAO;AAE1C,QAAM,oBAAoB,MAAM,OAAO,KAAK,MAAM,EAAE,SAAS,QAAQ,CAAC;AACtE,QAAM,oBAAoB,kBAAkB,MAAM,GAAG,EAAE,CAAC;AACxD,QAAM,mBAAmB,iBAAiB,MAAM,GAAG;AAEnD,aAAW,sBAAsB,kBAAkB;AACjD,UAAM,CAAC,SAAS,SAAS,IAAI,mBAAmB,MAAM,GAAG;AACzD,QAAI,YAAY,KAAM;AACtB,QAAI,gBAAgB,QAAQ,OAAO,SAAS,GAAG,QAAQ,OAAO,iBAAiB,CAAC,GAAG;AACjF,UAAI;AACF,eAAO,KAAK,MAAM,OAAO;AAAA,MAC3B,SAAS,GAAG;AACV,gBAAQ,MAAM,mBAAmB,OAAO;AACxC,cAAM,OAAO,gBAAgB,yBAAyB,EAAE,MAAM;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AACA,UAAQ,MAAM,6BAA6B;AAC3C,QAAM,OAAO,gBAAgB,2BAA2B,EAAE,MAAM;AAClE;","names":[]}