Webhooks
Verifying Webhooks

Verify Webhooks

Due to the intrinsic nature of webhooks, malicious entities can exploit vulnerabilities by sending counterfeit webhooks to an endpoint. Consider the simplicity of the scenario: an unfamiliar source initiating an HTTP POST request. This vulnerability poses a significant security risk for numerous applications or, at the very least, introduces a considerable potential for disruptions.

To mitigate this threat, Flex employs a security measure wherein each webhook, along with its accompanying metadata, is meticulously signed using an exclusive key designated for each endpoint. This cryptographic signature serves as an authentication mechanism, ensuring the legitimacy of the webhook's origin from Flex. As a result, only verified webhooks originating from Flex are processed, reinforcing the security framework and thwarting unauthorized access.

Verifying Signatures

Each webhook call includes three headers with additional information that are used for verification:

flex-event-id: the unique message identifier for the webhook message. This identifier is unique across all messages, but will be the same when the same webhook is being resent (e.g. due to a previous failure).

flex-timestamp: the unique message identifier for the webhook message. This identifier is unique across all messages, but will be the same when the same webhook is being resent (e.g. due to a previous failure).

flex-signature: the Base64 encoded list of signatures (space delimited).

The content to sign is composed by concatenating the id, timestamp and payload, separated by the full-stop character.

Flex uses an HMAC with SHA-256 to sign its webhooks.

So to calculate the expected signature, you should HMAC the signedcontent from above using the base64 portion of your signing secret (this is the part after the whsec prefix) as the key. For example, given the secret fwhsec_Y2NhZDczMDYtNDEyYi0xMWVlLTg5MTItNGY4Y2E5ZmU1MmI4 you will want to use Y2NhZDczMDYtNDEyYi0xMWVlLTg5MTItNGY4Y2E5ZmU1MmI4.

Here is an example of calculating the signature.

const crypto = require("crypto");
 
signedContent = `${flex_event_id}.${flex_timestamp}.${body}`;
const secret = "fwhsec_Y2NhZDczMDYtNDEyYi0xMWVlLTg5MTItNGY4Y2E5ZmU1MmI4";
 
// Need to base64 decode the secret
const secretBytes = Buffer.from(secret.split("_")[1], "base64");
const signature = crypto
  .createHmac("sha256", secretBytes)
  .update(signedContent)
  .digest("base64");
console.log(signature);

signature should match the contents of flex-signature specified in the header.