{"version":3,"file":"index.cjs","names":["Status","createHmac","timingSafeEqual"],"sources":["../../src/webhook/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node: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 = Number.parseInt(webhookTimestamp, 10);\n  if (Number.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":";;;;AAGA,MAAM,+BAA+B;AAErC,SAAS,gBAAgB,kBAA0B;CACjD,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CACxC,MAAM,YAAY,OAAO,SAAS,kBAAkB,EAAE;CACtD,IAAI,OAAO,MAAM,SAAS,GACxB,MAAMA,qBAAAA,OAAO,gBAAgB,2BAA2B,CAAC,CAAC,MAAM;CAElE,IAAI,YAAY,MAAM,8BACpB,MAAMA,qBAAAA,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,IAAI,YAAY,MAAM,8BACpB,MAAMA,qBAAAA,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,OAAO;AACT;;;;;;;;AASA,SAAgB,sBACd,SACA,SACA,QACG;CACH,MAAM,YAAY,QAAQ;CAC1B,MAAM,mBAAmB,QAAQ;CACjC,MAAM,mBAAmB,QAAQ;CACjC,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,kBACtC,MAAMA,qBAAAA,OAAO,gBAAgB,iBAAiB,CAAC,CAAC,MAAM;CAExD,MAAM,YAAY,gBAAgB,gBAAgB;CAElD,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,SAAS,QAAQ,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS;CAEpE,MAAM,UAAA,GADOC,YAAAA,WAAAA,CAAW,UAAU,OAAO,KAAK,QAAQ,QAAQ,CAC5C,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO;CAG1C,MAAM,oBAAoB,MADM,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ,IACzB,MAAM,GAAG,CAAC,CAAC;CACvD,MAAM,mBAAmB,iBAAiB,MAAM,GAAG;CAEnD,KAAK,MAAM,sBAAsB,kBAAkB;EACjD,MAAM,CAAC,SAAS,aAAa,mBAAmB,MAAM,GAAG;EACzD,IAAI,YAAY,MAAM;EACtB,KAAA,GAAIC,YAAAA,gBAAAA,CAAgB,QAAQ,OAAO,SAAS,GAAG,QAAQ,OAAO,iBAAiB,CAAC,GAC9E,IAAI;GACF,OAAO,KAAK,MAAM,OAAO;EAC3B,QAAQ;GACN,QAAQ,MAAM,mBAAmB,OAAO;GACxC,MAAMF,qBAAAA,OAAO,gBAAgB,yBAAyB,CAAC,CAAC,MAAM;EAChE;CAEJ;CACA,QAAQ,MAAM,6BAA6B;CAC3C,MAAMA,qBAAAA,OAAO,gBAAgB,2BAA2B,CAAC,CAAC,MAAM;AAClE"}