تأیید یک تحویل
`verify_webhook_signature`: با زمان ثابت، بههمراه یک پنجرهٔ بازپخش، و بازگرداندن رویداد تجزیهشده.
در یک هندلر درخواست
URL یک وبهوک عمومی است. هر چیزی روی اینترنت میتواند JSON با شکل درست را به آن POST کند، پس هندلری که بدون بررسی امضا payload['type'] را میخواند یک API نوشتنِ باز است.
import os from fastapi import FastAPI, Request, Responsefrom openemail import WebhookVerificationError, verify_webhook_signature app = FastAPI() @app.post('/webhooks/openemail')async def webhook(request: Request) -> Response: try: event = verify_webhook_signature( payload=await request.body(), headers=request.headers, secret=os.environ['OPENEMAIL_WEBHOOK_SECRET'], tolerance_seconds=300, ) except WebhookVerificationError: return Response('bad signature', status_code=400) print(event['type'], event['data']) return Response(status_code=204)بدنهٔ خام (RAW) را بدهید. تجزیه و سریالسازی دوباره، ترتیب کلیدها و فاصلهها را تغییر میدهد و امضا تطبیق نخواهد یافت. میتواند bytes یا str باشد: await request.body() در FastAPI، request.body در Django، request.get_data() در Flask. headers هر mapping ای را میگیرد، مانند request.headers در هر یک از این فریمورکها یا یک dict ساده، و X-OpenEmail-Signature با هر شکلی از بزرگی و کوچکی حروف پیدا میشود.
دو چیز که این تابع رسیدگی میکند و بررسی دستساز معمولاً نمیکند: MAC را در زمان ثابت مقایسه میکند، پس پیشوند درست را نمیتوان با زمانسنجی بازیابی کرد، و تحویلی را که بیش از tolerance_seconds در هر دو جهت کهنه باشد رد میکند، که مگر خلافش را بگویید پنج دقیقه است، تا یک درخواست ضبطشده برای همیشه قابل بازپخش نباشد. tolerance_seconds=0 بررسی بازپخش را خاموش میکند. هر دو باگ خاموشاند. هندلری با هرکدامشان از هر تستی که به فکرتان برسد سربلند بیرون میآید.
در هر شکستی WebhookVerificationError را raise میکند: یک secret خالی، نبودن هدر X-OpenEmail-Signature، هدری که به شکل t=<seconds>,v1=<hex> نباشد، مهر زمانی بیرون از پنجره، امضایی که تطبیق نیابد، یا بدنهای که JSON نباشد. در صورت موفقیت، بدنهٔ تجزیهشده را بهصورت یک WebhookPayload برمیگرداند، پس json.loads دومی نیست که اشتباه انجامش دهید. برای تایپ کردن data، نتیجه را annotate کنید، مثلاً با WebhookPayload[EmailOpenedData].
import os from openemail import verify_webhook_signaturefrom openemail.types import EmailOpenedData, WebhookPayload def on_open(body: bytes, headers: dict[str, str]) -> None: event: WebhookPayload[EmailOpenedData] = verify_webhook_signature( payload=body, headers=headers, secret=os.environ['OPENEMAIL_WEBHOOK_SECRET'], ) print(event['data']['recipient'], event['data']['first'], event['data']['country'])WebhookVerificationError زیرکلاس هر دوی OpenEmailError و ValueError است. این تابع یک تابع ساده است نه یک coroutine، پس آن را بدون await هم از کد همگام و هم از کد ناهمگام فراخوانی کنید. به چیزی جز hmac و hashlib از کتابخانهٔ استاندارد نیاز ندارد.