Setting up a receiver for DFO webhooks. Need to verify the signature in the X-CXone-Signature header to stop replay attacks. The docs say it’s HMAC SHA256 using the shared secret, but my verification always fails.
Here’s the Node.js snippet I’m using:
const crypto = require('crypto');
app.post('/webhook', (req, res) => {
const signature = req.headers['x-cxone-signature'];
const payload = JSON.stringify(req.body);
const secret = process.env.WEBHOOK_SECRET;
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
const digest = hmac.digest('hex');
if (signature !== digest) {
return res.status(401).send('Invalid signature');
}
res.status(200).send('OK');
});
The payload string matches exactly what I see in the request body. The secret is correct. I’ve tried using the raw buffer instead of hex, same result. Is CXone including the content-type or timestamp in the signed string? Nothing in the DFO API docs clarifies the exact string-to-sign format.