किसी delivery का सत्यापन
`verifyWebhookSignature`: स्थिर-समय में, replay विंडो के साथ, और पार्स किया गया event वापस।
किसी request handler में
webhook URL सार्वजनिक होता है। इंटरनेट पर कोई भी चीज़ उस पर सही आकार वाला JSON POST कर सकती है, इसलिए वह handler जो signature जाँचे बिना payload.type पढ़ता है, एक खुला write API है।
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 })}RAW body भेजें। पार्स करके दोबारा serialise करने से key का क्रम और whitespace बदल जाते हैं, और signature मेल नहीं खाएगा। headers एक Headers object लेता है या Node के req.headers जैसा सादा object, और X-OpenEmail-Signature चाहे जिस case में हो, मिल जाता है।
दो चीज़ें जो यह संभालता है और हाथ से लिखी जाँच आमतौर पर नहीं: यह MAC की तुलना स्थिर समय में करता है, इसलिए समय नापकर सही उपसर्ग निकाला नहीं जा सकता, और यह किसी भी दिशा में toleranceSeconds से पुरानी delivery अस्वीकार कर देता है — जब तक आप कुछ और न कहें, पाँच मिनट — इसलिए पकड़ा गया request हमेशा के लिए replay करने लायक नहीं रहता। toleranceSeconds: 0 replay जाँच बंद कर देता है। दोनों बग चुप हैं। जिस handler में इनमें से कोई एक भी हो, वह हर उस टेस्ट में पास हो जाता है जो आपके मन में आएगा।
यह हर विफलता पर throw करता है: अनुपस्थित X-OpenEmail-Signature header, ऐसा header जो t=<seconds>,v1=<hex> रूप में नहीं है, विंडो से बाहर का timestamp, या ऐसा signature जो मेल नहीं खाता। सफलता पर यह body को WebhookPayload के रूप में पार्स करके resolve करता है, इसलिए गलत करने को कोई दूसरा JSON.parse बचता ही नहीं। data को type देने के लिए EmailOpenedData जैसा type argument भेजें।
इसे globalThis.crypto.subtle चाहिए, जो Node 20+, Bun, Deno और Cloudflare Workers सभी देते हैं।