With webhooks, you receive programmatic notifications when changes happen in Blue Canvas. You can register a webhook on the settings page.
Creating a Webhook Subscription
- From Blue Canvas, go to Settings > Webhooks.
- Click on New Webhook.
- Enter the URL of your event listener, and click Save.
- You can reveal the Signing Secret to use in the next step.
Verifying authenticity
When your API gets requested by Blue Canvas, we always include the X-Bluecanvas-Signature-HS256
header. This header contains a HMAC (Hash-Based Message Authentication Code) signature using SHA-256 of the request.
Encoding is done in the following way:
time_safe_compare(valueInHeader, base64_encode(sha256_hmac(SIGNING_SECRET, REQUEST_BODY)))
public class BlueCanvasSignatureVerifier {
public static Boolean verify(String webhookSecret, String requestBody, String requestSignatureFromHeader) {
Blob privateKey = Blob.valueOf(webhookSecret);
Blob data = Blob.valueOf(requestBody);
Blob macToVerify = EncodingUtil.base64Decode(requestSignatureFromHeader);
return Crypto.verifyHMac('hmacSHA256', data, privateKey, macToVerify);
}
@IsTest
static void testSign() {
// TODO Move to test class for coverage
System.assertEquals(
'yHe0ALeSA8vdSagOvh6bNCtOQCBY9R6tr5xQfJH69ng=',
BlueCanvasSignatureVerifier.sign(
'ExampleSecretJustForTesting',
'{"example": "Please do not alter the JSON formatting, the body should be used as-is"}'
)
);
}
}
import hmac
import hashlib
from base64 import b64decode
def verify_hash(
webhook_secret: str, request_body: str, request_signature_from_header: str
) -> bool:
private_key = webhook_secret.encode()
data = request_body.encode()
digest = hmac.new(private_key, data, hashlib.sha256).digest()
expected_digest = b64decode(request_signature_from_header)
return hmac.compare_digest(digest, expected_digest)
# TODO Remove the line below
print(
verify_hash(
"ExampleSecretJustForTesting",
'{"example": "Please do not alter the JSON formatting, the body should be used as-is"}',
"yHe0ALeSA8vdSagOvh6bNCtOQCBY9R6tr5xQfJH69ng=",
)
)
// You can also use verifyHMac from our SDK
export function verifyHMac(
webhookSecret: string,
requestBody: Buffer | string,
requestSignatureFromHeader: string
): boolean {
const hmac = createHmac("sha256", webhookSecret).update(requestBody);
const digest = hmac.digest();
const expectedDigest = Buffer.from(requestSignatureFromHeader, "base64");
if (digest.length !== expectedDigest.length) {
return false;
}
return timingSafeEqual(digest, expectedDigest);
}
describe("Utils", () => {
it("Can verify a webhook signature", () => {
const result = verifyHMac(
"ExampleSecretJustForTesting",
'{"example": "Please do not alter the JSON formatting, the body should be used as-is"}',
"yHe0ALeSA8vdSagOvh6bNCtOQCBY9R6tr5xQfJH69ng="
);
expect(result).toBe(true);
});
});
You can test with the following data (please note that none of them end with a new-line character):
ExampleSecretJustForTesting
{"example": "Please do not alter the JSON formatting, the body should be used as-is"}
yHe0ALeSA8vdSagOvh6bNCtOQCBY9R6tr5xQfJH69ng=
More about the used technologies
Security consideration
Please make sure you use HTTPS in order to prevent replay attacks or make sure you do not execute duplicate actions.
Tips and tricks
If you like inspect the webhooks before starting your integration we recommend using webhook.site note that we are not affiliated with this app.