Webhooks
Signature Validation Guide
Introduction
To ensure the security and integrity of the data we send to your applications via webhooks, we implement signature verification. This document provides a step-by-step guide on how you can validate these webhook signatures using a secret key that we provide.
Requesting a Secret Key
To start receiving and validating webhook notifications, you first need a unique secret key:
- Request a Secret Key:
- If you have access to our Backoffice System click in your Avatar Icon and go to -> Profile Page
- There you can see your Webhook Secret.
- Contact our support team or use your customer dashboard (if available) to request a secret key for webhook verification.
- Update your secret by making a request to
POST /v1/integrators/{integratorId}/update-webhook-secret
. You will receive a 200 if successful, then you can view the secret in the Backoffice as listed in step i.
- If you have access to our Backoffice System click in your Avatar Icon and go to -> Profile Page
- Receive Your Key Securely: We will provide you with a unique secret key through a secure channel. This key is unique to your application and should not be shared.
Understanding Webhook Notifications Structure
Payload Produced Example
{"operation":"BENEFICIARY_KYC","resourceId":"27412213-a551-48f8-a9d6-61eab2f2bd6c","createdAt":[2024,3,15,19,23,59,401139000],"integratorId":"65f0a48a79ae4e2dd70e72e2","beneficiaryId":"65f0a49179ae4e2dd70e72e4","message":null,"success":null,"data":{"id":"d356b30e-dae5-49c7-b776-d28589fc6b92","simulationId":"8a80fa558e33f7c7018e3403f9600000","integratorId":"65f0a48a79ae4e2dd70e72e2","beneficiaryId":"65f0a49179ae4e2dd70e72e4","from":{"currencyCode":"BRL","value":402.9},"to":{"currencyCode":"USDC","value":79.29},"type":"ON_RAMP","status":"SUCCEEDED","marketExchangeRate":4.9603,"exchangeRate":4.9603,"fees":{"currencyCode":"BRL","value":8.058},"finalTransactionHash":null,"effectiveTransactionValue":5.0808}}
Integrator Callback Request Attributes
Property Name | Type | Description |
---|---|---|
operation | IntegratorOperation | The type of operation that the callback is related to. This is an enumeration of possible operations. |
resourceId | String | A unique identifier for the resource that is relevant to the callback event. |
createdAt | LocalDateTime | The date and time when the event that triggered the callback occurred. Serialized using LocalDateTimeSerializer . |
integratorId | String | The unique identifier for the integrator. This helps in identifying which integrator the callback is associated with. |
beneficiaryId | String | The unique identifier for the beneficiary related to the callback event. |
message | String | A human-readable message providing additional information about the callback event. |
success | Boolean | Indicates whether the operation was successful (true ) or not (false ). |
data | T (Generic Type) | Additional data related to the callback event. The type of this data is generic and can vary. |
IntegratorOperation Enumerator
The IntegratorOperation
enumeration within IntegratorCallbackRequest
defines the types of operations that can trigger a callback:
- ON_RAMP: Represents an on-ramp operation.
- OFF_RAMP: Represents an off-ramp operation.
- PAYMENT_IN: Represents a payment-in operation.
- PAYMENT_OUT: Represents a payment-out operation.
- BENEFICIARY_BANK_ACCOUNT_VALIDATION: Represents a beneficiary bank account validation operation.
- BENEFICIARY_KYC: Represents a Know Your Customer (KYC) operation for a beneficiary.
- BENEFICIARY_KYB: Represents a Know Your Business (KYB) operation for a beneficiary.
- BENEFICIARY_APPROVED: Represents an operation indicating the approval of a beneficiary.
Verifying Our Webhooks Via Signature
We generate our webhook signatures based in a random secret key provided to our integrators and the request payload above.
Signature Example
Signature example based in the payload above:
AbyU13J826tKxR2G5KWy8X46agiqnxaGuNaFjcf5bRI=
Verifying the Signature in Your Application
- Extract the Signature: Retrieve the signature from the
X-Caliza-Webhook-Signature
header of the incoming request. - Get the Payload: Read the payload of the webhook.
- IMPORTANT: The payload must to be the exact and unmodified request body.
- Call
verifySignature
: Use the extracted signature, payload, and your secret key to validate the signature.
Implementing Signature Verification
Java Example
Use the following Java method to verify the signature of incoming webhook requests:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class WebhookSignatureVerifier {
/**
* Verifies a HMAC SHA-256 signature for a given payload using a secret key.
* This method compares the received signature with a freshly computed one to validate webhook data integrity and authenticity.
*
* @param payload The payload for which the signature was generated, the body of the webhook request.
* @param receivedSignature The signature received in the webhook request, usually found in a request header.
* @param secret The secret key provided by us for each integrator - the same used to generate the signature.
* @return true if the received signature matches the computed signature, false otherwise.
* @throws NoSuchAlgorithmException If the SHA-256 hashing algorithm is not available.
* @throws InvalidKeyException If the given secret key is invalid.
*/
public boolean verifySignature(HttpServletRequest httpRequest, String secret) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
String payload = IOUtils.toString(httpRequest.getReader());
String receivedSignature = httpRequest.getHeader("X-Caliza-Webhook-Signature");
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secretKey);
byte[] hash = sha256_HMAC.doFinal(payload.getBytes());
String calculatedSignature = Base64.getEncoder().encodeToString(hash);
return MessageDigest.isEqual(calculatedSignature.getBytes(), receivedSignature.getBytes());
}
}
When you receive a webhook, call verifySignature
with the payload, the signature you received in the webhook header, and your secret key.
JavaScript Implementation using Node.js - using native http module
const http = require('http');
const crypto = require('crypto')
const secret = "my_webhook_secret";
//Extract the raw body
const server = http.createServer((req, res) => {
const { headers, method, url } = req;
if (req.url === '/your-endpoint' && req.method === 'POST') {
// Listen for data events to collect the chunks of data.
req.on('data', chunk => {
//Get the raw body
req.rawBody = chunk.toString();
});
req.on('end', () => {
// verify signature
var result = verifySignature(req, secret);
// build http response
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
const responseBody = { headers, method, url, result };
res.write(JSON.stringify(responseBody));
res.end();
});
} else {
res.writeHead(404);
res.end();
}
});;
/**
* Validates the integrity of a webhook request by comparing the received signature with a calculated signature.
* The function extracts the signature from the 'x-caliza-webhook-signature' HTTP header of the incoming request,
* then generates a new signature using the request's raw body and a secret key. It uses the HMAC (Hash-Based Message Authentication Code)
* algorithm with SHA-256 as the hash function to compute the signature. The calculated signature is then compared
* to the received signature to verify the request's integrity. This method is essential for ensuring that the webhook
* request is from a trusted source and has not been tampered with during transmission.
*/
function verifySignature(request, secret) {
//Extract Signature header
let receivedSignature = request.headers['x-caliza-webhook-signature'];
let hmac = crypto.createHmac('sha256', secret);
hmac.update(request.rawBody);
let calculatedSignature = hmac.digest('base64');
return calculatedSignature === receivedSignature;
}
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
JavaScript Implementation using Express.js
const express = require('express');
const bodyParser = require("body-parser");
const crypto = require("crypto");
const app = express();
const secret = "my_webhook_secret";
//Extract the raw body
app.use(
bodyParser.json({
verify: (req, res, buf, encoding) => {
if (buf && buf.length) {
req.rawBody = buf.toString(encoding || "utf8");
}
},
}),
);
app.post('/your-endpoint', (req, res) => {
var result = verifySignature(req, secret);
res.send(result);
});
/**
* Validates the integrity of a webhook request by comparing the received signature with a calculated signature.
* The function extracts the signature from the 'x-caliza-webhook-signature' HTTP header of the incoming request,
* then generates a new signature using the request's raw body and a secret key. It uses the HMAC (Hash-Based Message Authentication Code)
* algorithm with SHA-256 as the hash function to compute the signature. The calculated signature is then compared
* to the received signature to verify the request's integrity. This method is essential for ensuring that the webhook
* request is from a trusted source and has not been tampered with during transmission.
*/
function verifySignature(request, secret) {
//Extract Signature header
let receivedSignature = request.headers['x-caliza-webhook-signature'];
let hmac = crypto.createHmac('sha256', secret);
hmac.update(request.rawBody);
let calculatedSignature = hmac.digest('base64');
return calculatedSignature === receivedSignature;
}
app.listen(3001, () => {
console.log('Server running on http://localhost:3000');
});
module.exports = app;
Best Practices for Managing Your Secret Key
- Secure Storage: Store your secret key securely. Avoid hardcoding it in your application code or storing it in plain text files.
- Limit Access: Only personnel who need to configure webhook processing should have access to the secret key.
- Regular Rotation: Regularly request a new secret key to minimize the risk of unauthorized access.
- Monitor Usage: Monitor the usage of your webhooks and be alert to any unusual patterns or failed validation attempts.
Need Help?
If you encounter any issues or need further assistance, please contact our support team.
Updated 2 months ago