HMAC Signature

HMAC (Hash-based Message Authentication Code) is a method used to authenticate a message using a shared secret and a hash function. It allows our clients to verify that the received webhook is from a trusted source (Deck) and that the information wasn’t tampered with, in which case the signature won’t match the shared secret. We use SHA-256, so trying to verify the hash with anything else will not work.

Where it is

An HMAC key is generated automatically when creating a new Team. To access it, go to your Dashboard and select a team. Click on “Team Settings” in the menu on the left side of the screen, then go into the Branding tab. The HMAC secret will be at the bottom. The HMAC Key is the same for both Live and Sandbox environments.

How it will look in a Webhook

Any Webhook that will be sent to the client, assuming they use a Team with HMAC enabled, will include an X-Signature header containing the HMAC hash of the payload. This signature can be validated using the Team’s HMAC key.

Type of HMAC

The HMAC is in Base64. Note that Base64 is used to encode the raw binary output of the HMAC hash into a text-safe string so it can be sent via HTTP headers or stored easily. HMAC is not used to encrypt the rest of the information passed through the webhook, but its presence does not affect the encryption already in place.

Code to verify the HMAC

To verify that the HMAC received matches the one for your Team, you can use the following code examples.
const crypto = require("crypto");

// Provided Base64-encoded secret
const secretBase64 = <<YOUR_DECK_WEBHOOK_SECRET_FROM_DASHBOARD>>;

// JSON body payload as a raw string (must match byte-for-byte) - This is AN EXAMPLE Webhook Event Body
const body = `{"link_token":"link-sandbox-77bd1897-b21b-4eb8-fca6-08dda362f7ed","public_token":"public-sandbox-b7500d0c-920d-46cf-44eb-08dda3634c66","webhook_type":"Link","webhook_code":"ConnectionCreated","environment":"Sandbox"}`;

// Decode the Base64 secret into a raw byte buffer
const key = Buffer.from(secretBase64, "base64");

// Create the HMAC hash
const hmac = crypto.createHmac("sha256", key);
hmac.update(body);
const computedSignature = hmac.digest("base64");

// Output
console.log("Expected Signature:", <<X_SIGNATURE_FROM_HEADER>>);
console.log("Computed Signature:", computedSignature);
Make sure that the Expected Signature and Computed Signature match.