Webhook Signature Verification

When building an element that supports webhook events, the provider may send a signature in the header of the webhook to verify that it is genuine. For example:

The requests from MyApp (the application you are building an element for) to your system (Cloud Elements) will be signed. The key for the signature will be either the account token or the oauth application secret, depending on the type of integration. The algorithm used for creating the signature is HMAC SHA256 on the payload of the request and the signature itself is sent on the X-MyElement-Signature header.

This verification will happen on a per webhook basis within the event hook in the element.  Use the JavaScript below as a starting point.

// The headers for the inbound webhook are stored on the events object in the event hook context
const incomingSignature = events.eventHeaders['X-MyApp-Signature'];
// This could be the token used for API calls or a specific secret for webhooks
// it will be used to generate the signature
const key = configuration["webhook.callback.secret"];
/*
The events object in the event hook context contains the inbound webhook payload,
as well as, the event headers and the ID generated by Cloud Elements.
The provider is using the payload + the key to generate the signature,
so the event headers and event ID need to be removed from that object before generating the
signature to compare.
*/
const incomingPayload = Object.assign({}, events);
delete incomingPayload.eventId;
delete incomingPayload.eventHeaders;
const payload = JSON.stringify(incomingPayload);
// CE.hmac is a wrapper around Node.js Crypto
if (incomingSignature == CE.hmac('sha256')('Base64')(key, payload)) {
// Once inside the if block the signature is validated,
// format the event as per normal
const formattedEvents = [];
const eventObj = {};
// Parse the date, object ID, event type and object from the incoming payload
// Event type needs to be CREATED, UPDATED, or DELETED
eventObj.event_date = new Date().toISOString();
eventObj.event_object_id = events["content"]["id"];
eventObj.event_type = events["action"];
eventObj.event_object_type = events["object"];
formattedEvents.push(eventObj);
done({ "events": formattedEvents });
}