Verifying the signature

Webhooks

Verifying the signature

This section is for your developer.

Anyone who knows your URL could post to it. Checking the signature proves a message came from your store.

How it's made

X-Trooply-Signature = sha256= + the lowercase hex HMAC-SHA256 of the raw request body, using the webhook's Signing Secret (as plain text) as the key.

Three things to get right:

  1. Sign the raw body bytes exactly as received. Don't parse and re-serialise the JSON first.
  2. Use the secret as text; don't decode it from hex.
  3. Include the sha256= prefix when comparing.

Example (Node.js)

const crypto = require("crypto");
app.post("/hooks/store", express.raw({ type: "application/json" }), (req, res) => {
  const expected = "sha256=" + crypto
    .createHmac("sha256", process.env.WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");
  const got = req.get("X-Trooply-Signature") || "";
  if (got.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))) {
    return res.status(401).send("bad signature");
  }
  const event = JSON.parse(req.body.toString("utf8"));
  // handle event.event / event.payload
  res.json({ ok: true });
});

Example (Python)

import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header or "")

Lost the secret? Delete the webhook and create a new one; a new secret is generated.