Перейти к документации
SDK

Проверка доставки

`verifyWebhookSignature`: сравнение за постоянное время, окно защиты от повторов и разобранное событие на выходе.

В обработчике запроса

URL вебхука публичен. Что угодно в интернете может отправить на него POST с JSON нужной формы, поэтому обработчик, который читает payload.type без проверки подписи, — это открытый API на запись.

webhook-handler.ts
import { verifyWebhookSignature } from '@openemail/sdk' export async function POST(request: Request) {  try {    const event = await verifyWebhookSignature({      payload: await request.text(),      headers: request.headers,      secret: process.env.OPENEMAIL_WEBHOOK_SECRET!,      toleranceSeconds: 300,    })     console.log(event.type, event.data)  } catch {    return new Response('bad signature', { status: 400 })  }   return new Response(null, { status: 204 })}

Передавайте СЫРОЕ тело. Разбор и повторная сериализация меняют порядок ключей и пробелы, и подпись не сойдётся. headers принимает объект Headers или обычный объект, например req.headers из Node, а X-OpenEmail-Signature находится в любом регистре.

Две вещи, которые здесь учтены и которых обычно нет в самодельной проверке: MAC сравнивается за постоянное время, поэтому верный префикс нельзя восстановить по замерам времени, и доставка старше toleranceSeconds в любую сторону отклоняется — пять минут, если вы не указали иное, — чтобы перехваченный запрос нельзя было воспроизводить вечно. toleranceSeconds: 0 отключает проверку на повтор. Обе ошибки безмолвны. Обработчик с любой из них проходит все тесты, которые вам пришло бы в голову написать.

Он выбрасывает исключение при любом сбое: отсутствующий заголовок X-OpenEmail-Signature, заголовок не в форме t=<seconds>,v1=<hex>, отметка времени вне окна или несовпадающая подпись. При успехе он разрешается телом, разобранным как WebhookPayload, поэтому второго JSON.parse, в котором можно ошибиться, нет. Передайте аргумент типа, например EmailOpenedData, чтобы типизировать data.

Ему нужен globalThis.crypto.subtle, который предоставляют Node 20+, Bun, Deno и Cloudflare Workers.