Bỏ qua tới phần tài liệu
SDK

Xác minh một lần gửi

`verifyWebhookSignature`: so sánh trong thời gian hằng định, có cửa sổ chống phát lại, và trả về sự kiện đã được phân tích.

Trong trình xử lý yêu cầu

URL webhook là công khai. Bất kỳ ai trên internet cũng có thể POST JSON đúng cấu trúc tới nó, nên một handler đọc payload.type mà không kiểm tra chữ ký chính là một API ghi mở toang.

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 })}

Hãy truyền body THÔ. Việc phân tích rồi tuần tự hóa lại sẽ thay đổi thứ tự khóa và khoảng trắng, khiến chữ ký không khớp. headers nhận một đối tượng Headers hoặc một object thông thường như req.headers của Node, và X-OpenEmail-Signature được tìm thấy bất kể viết hoa hay viết thường.

Hàm này xử lý hai điều mà một bước kiểm tra tự viết thường bỏ sót: nó so sánh MAC trong thời gian hằng định, nên không thể khôi phục tiền tố đúng bằng cách đo thời gian, và nó từ chối một lần gửi lệch quá toleranceSeconds theo cả hai hướng, mặc định là năm phút trừ khi bạn chỉ định khác, nên một yêu cầu bị bắt được không thể bị phát lại mãi mãi. toleranceSeconds: 0 tắt kiểm tra phát lại. Cả hai lỗi đều âm thầm. Một handler mắc một trong hai lỗi này vẫn vượt qua mọi bài kiểm thử mà bạn nghĩ ra để viết.

Hàm ném lỗi ở mọi trường hợp thất bại: thiếu header X-OpenEmail-Signature, header không có dạng t=<seconds>,v1=<hex>, dấu thời gian nằm ngoài cửa sổ, hoặc chữ ký không khớp. Khi thành công, nó resolve thành body đã được phân tích dưới dạng WebhookPayload, nên bạn không phải gọi JSON.parse lần thứ hai và có nguy cơ làm sai. Truyền một đối số kiểu như EmailOpenedData để gán kiểu cho data.

Hàm cần globalThis.crypto.subtle, thứ mà Node 20+, Bun, Deno và Cloudflare Workers đều cung cấp.