Ga direct naar de documentatie
SDK

Een bezorging verifiëren

`verifyWebhookSignature`: constante tijd, met een replayvenster, en het geparste event terug.

In een request handler

Een webhook-URL is openbaar. Alles op internet kan er JSON met de juiste vorm naartoe POSTen, dus een handler die payload.type leest zonder de signature te controleren is een open schrijf-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 })}

Geef de RUWE body door. Parsen en opnieuw serialiseren verandert de sleutelvolgorde en de witruimte, en dan klopt de signature niet meer. headers neemt een Headers-object aan of een gewoon object zoals req.headers van Node, en X-OpenEmail-Signature wordt gevonden ongeacht hoofdletters of kleine letters.

Twee dingen die dit afhandelt en een zelfgebouwde controle meestal niet: het vergelijkt de MAC in constante tijd, zodat het juiste voorvoegsel niet via timing te achterhalen is, en het weigert een bezorging die meer dan toleranceSeconds oud is in beide richtingen, vijf minuten tenzij je anders opgeeft, zodat een onderschept verzoek niet eeuwig opnieuw af te spelen is. toleranceSeconds: 0 zet de replaycontrole uit. Beide bugs zijn stil. Een handler met een van beide slaagt voor elke test die je zou bedenken om te schrijven.

Het gooit bij elke fout: een ontbrekende X-OpenEmail-Signature-header, een header die niet de vorm t=<seconds>,v1=<hex> heeft, een timestamp buiten het venster, of een signature die niet klopt. Bij succes levert het de body op, geparst als een WebhookPayload, zodat er geen tweede JSON.parse is die je fout kunt doen. Geef een typeargument zoals EmailOpenedData mee om data te typeren.

Het heeft globalThis.crypto.subtle nodig, dat Node 20+, Bun, Deno en Cloudflare Workers allemaal bieden.