To ensure the authenticity and integrity of webhook payloads, we include a Rivo-Signature in the headers of each webhook event. The Rivo-Signature is generated using the HMAC-SHA256 hash function and encoded in Base64.
To validate the webhook payload, follow these steps:
- Retrieve the Rivo-Signature from the webhook headers.
- Calculate the HMAC-SHA256 hash of the raw webhook payload using your secret token. Ensure you use the raw, unmodified payload. (You can view your webhook's secret token by clicking the 'edit' button once it's been created)
- Encode the resulting hash in Base64.
- Compare the calculated Base64-encoded hash with the received Rivo-Signature.
- If the two match, the webhook payload is valid and can be processed.
Here's a code example in JavaScript to demonstrate the verification process:
const crypto = require('crypto');
function verifySignature(payload, secretToken, receivedSignature) {
// Create HMAC-SHA256 signature
const hmac = crypto.createHmac('sha256', secretToken);
const calculatedSignature = hmac.update(payload).digest('base64'); // Use Base64 encoding
// Compare signatures securely to avoid timing attacks
return crypto.timingSafeEqual(
Buffer.from(receivedSignature, 'base64'),
Buffer.from(calculatedSignature, 'base64')
);
}
// Example usage
const rawPayload = '{"id":122573686,"points_amount":0,...}'; // Raw JSON payload as a string
const secretToken = 'your-secret-token'; // Replace with your actual webhook secret
const receivedSignature = '258k8V79x5Lzsa/McBRrCJO30rTm4oCP/K55ZPkG4Tg='; // From Rivo-Signature header
if (verifySignature(rawPayload, secretToken, receivedSignature)) {
console.log('Webhook verified successfully!');
} else {
console.log('Invalid webhook signature!');
}
Make sure to replace the secretToken and payload with your actual webhook secret and the raw payload received in the webhook request.
Key Notes
- Encoding: The Rivo-Signature is Base64-encoded, so your calculated signature must use the same encoding.
- Raw Payload: Always use the raw event.body string for signature calculation. Avoid parsing or re-stringifying the payload, as it may introduce discrepancies.
- Timing-Safe Comparison: Use crypto.timingSafeEqual to prevent timing attacks when comparing the calculated and received signatures.