Review

Verify KegWatch inventory webhook signatures

Craft-brewery POS now receives live keg-level webhooks from KegWatch sensors. Rejects unsigned payloads before enqueuing inventory apply jobs so spoofed empties cannot zero out taps mid-service.

PythonTier 3securitywebhookscrypto

Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.

services/webhooks/kegwatch.py+18-5
1414import hashlib
1515import hmac
1616from fastapi import Header, HTTPException, Request
1717
1818from app.config import settings
1919from app.jobs import enqueue_keg_delta
2020
21+def _expected_signature(body: bytes) -> str:
22+ return hmac.new(
23+ settings.KEGWATCH_WEBHOOK_SECRET.encode("utf-8"),
24+ body,
25+ hashlib.sha256,
26+ ).hexdigest()
27+
21-@router.post("/webhooks/kegwatch")
22-async def kegwatch_webhook(request: Request):
23- body = await request.json()
24- enqueue_keg_delta(body)
25- return {"ok": True}
28+@router.post("/webhooks/kegwatch")
29+async def kegwatch_webhook(
30+ request: Request,
31+ x_kegwatch_signature: str = Header(...),
32+):
33+ raw = await request.body()
34+ expected = _expected_signature(raw)
35+ if x_kegwatch_signature != expected:
36+ raise HTTPException(status_code=401, detail="invalid signature")
37+ enqueue_keg_delta(await request.json())
38+ return {"ok": True}