Skip to main content

Verify Webhook Signature

Overview#

Sunbit provides an option to verify webhook requests sent to your endpoints. Every webhook request will include a signature in the request's header. You can use this signature in your code to make sure the request was actually sent by Sunbit.

Before you start, you will need your webhook's signature. You can find it on the Sunbit developer portal under the Webhook section, the signature will be automatically generated when you set your Webhook endpoint.

Webhooks contain a header called "Sunbit-Signature" with the timestamp and a signature. The timestamp is prefixed by t= and the signature is prefixed by a scheme. Schemes start with v, followed by an integer. Currently, the only valid signature scheme is v1. Sunbit generates signatures using HMAC with SHA2-256.

Header example:

Sunbit-Signature: t=1565220904,v1=20c75c1180c701ee8a796e81507cfd5c932fc17cf63a4a55566fd38da3a2d3d2

Verify Webhook signature#

Step 1: Extract the timestamp and signatures from the header#

Split the header, using the , character as the separator, to get a list of elements. Split each element using the = character as the separator, to get a prefix and value pair. The value t represents the timestamp (in seconds), and v1 represents the signature.

Step 2: Prepare the signed_payload string#

The signed_payload string is created by concatenating:

  • The timestamp (as a string)
  • The character .
  • The actual JSON payload (i.e., the request body)
  • Step 3: Determine the expected signature#

    Use the 3 components from Step 2 along with the Sunbit Signature to compute an HMAC with the SHA256 hash function. Depending on the language that you are using this will look something like the following:

    secret = 'sunbit webhook signature' // the webhook signature from Sunbit Developers Portaltimestamp = sunbitSignatureHeader.substring(between 't=' and ',')payload = timestamp + "." + requestBodyexpectedSignature = hmacSHA256(payload, secret)signature = sunbitSignatureHeader.substring(after 'v1=')if (signature != expectedSignature) throw error

    Step 4: Compare the signatures#

    Compare the signature in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance (recommended - up to 5 Min)

    Examples#

    Javascript working example
    const crypto = require('crypto');
    //Constantsconst headerRegex = /t=(?<timestamp>\d{10}),v1=(?<signature>[a-f0-9]+)/;const secret = 'DwS3QStMkgKziZxd9NXcvqFkxP4JNA3i';
    //Header from request: sunbit-signatureconst header = 't=1643444288,v1=e1bfa98d067faeea521387c8917b71c96e32e1f9028a3b0b2167c4c7408cdacb';
    //Read header using Regexconst found = header.match(headerRegex);
    //Extract the timestampconst timestamp = found.groups.timestamp;
    //Construct payload from webhook bodyconst payload = timestamp + "." + JSON.stringify({    "eventType": "MERCHANT_CREATED",    "payload": {      "location": "Merchant location",      "url": "merchant/application/url",      "statusReason": "NONE"    }  });
    //Create HMAC from secret and payloadconst hmac = crypto.createHmac('sha256', secret);const data = hmac.update(payload, 'utf8');const gen_hmac= data.digest('hex');
    //Check for validityif(gen_hmac ===  found.groups.signature){    console.log('Valid signature');} else {    console.log('Invalid signature');}
    //Printing the output on the consoleconsole.log("hmac : " + gen_hmac);