Worxley for Developers

Verifying signatures

Check every delivery's HMAC-SHA256 signature and timestamp before trusting it.

The signature#

X-Worxley-Signature is t=<unix seconds>,v1=<hex>, where v1 is an HMAC-SHA256 of t + "." + raw body keyed with your endpoint’s secret.

  • Use the raw request body, before any JSON parsing.
  • Reject timestamps more than 5 minutes old so a captured request can’t be replayed.

Example#

const crypto = require("crypto");

function verifyWorxley(rawBody, header, secret, toleranceSec = 300) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
  if (!m) return false;
  const [, t, v1] = m;
  if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSec) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

// Express: keep the raw body for this route
app.post("/worxley-webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyWorxley(req.body.toString("utf8"), req.get("X-Worxley-Signature"), process.env.WORXLEY_WEBHOOK_SECRET)) {
    return res.sendStatus(400);
  }
  const event = JSON.parse(req.body);
  queue.add(event); // handle later, dedupe on event.id
  res.sendStatus(204);
});

Rotating a secret#

Rotate a secret from the endpoint’s menu. The new secret applies from the next delivery, so update your server straight after.