How to Authenticate a Webhook?
How to authenticate a webhook
Once you have received the payload from the webhook, it needs to be confirmed that it did indeed come from Bracken.
We can use the header of the payload to confirm that it came from Bracken by encrypting the secret that we created with the JSON body payload, using the HMACSHA256 algorithm which produces a binary digest, then we use base64 to encode it as a string output.
Then by matching it to the already encrypted header of the payload, it means that if the two signatures match it the payload came from Bracken.
Note: This is why we suggest to use a secure secret when signing up for our webhooks because it is used to authenticate the response.
JavaScript Example Code
const express = require('express');
const app = express(); //app is an instance of express.
// library to assist with encryption
const crypto = require('crypto');
// same secret you declared when creating the webhook
const secret = "12345";
// the front of the header will always have this.
const front = "HMACSHA256 ";
//app.METHOD(PATH, HANDLER)
app.post('/webhooks', function (req, res) {
const hash = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(req.body))
.digest('base64');
const yourSignature = front + hash;
const brackenWebhookSignature = req.header('Authorization');
if ((brackenWebhookSignature != null) && brackenWebhookSignature == yourSignature) {
res.send('OK the signatures match')
} else {
return res.status(403).send('Do not trust, not from a Bracken webhook');
}
});
Javascript Code Explanation
We will now receive ‘OK the signatures match’ if the webhook comes from Bracken and ‘Do not trust, not from a Bracken webhook’ if the response is not from Bracken.
In the code above we have encrypted the secret and the JSON body using the HMACSHA256 algorithm which produces a binary digest, then we use base64 to encode it as a string output.
To do this we used crypto
which is a Node.js module that provides cryptographic functionality.
We declare it at the start of the file
const crypto = require('crypto');
and then use it to hash the secret and JSON body within the method that catches the incoming webhook responses.
We do this by creating a constant called hash
using the crypto
module.
First we create an Hash-Based Message Authentication Code (HMAC) specifying the HMACSHA256 algorithm to encrypt the secret.
const hash = crypto
.createHmac('sha256', secret)
Then we update the hash
to include the JSON body of the response.
.update(JSON.stringify(req.body))
Lastly we encode the hash
using base64
encoding, because need to encode the binary data that the HMACSHA256 algorithm produces and convert it to textual data, so that it is comparable to our response header.
.digest('base64');
Now we have encrypted our secret and the JSON body from the response that we received. Next in the code we are comparing if our signature matches the Authorization header from the response we received.
const yourSignature = front + hash;
const brackenwebhooksignature = req.header('Authorization');
if ((brackenwebhooksignature != null) && brackenwebhooksignature == yourSignature) {
res.send('OK the signatures match')
} else {
return res.status(403).send('Do not trust, not from a Bracken webhook');
}
If the signatures match then you have successfully authenticated that the webhook response came from Bracken.
C# Example Code
//using this to get the header payload from the response
using System.Net.Http.Headers;
public async Task<IActionResult> VerifyHook()
{
// declare your secret
string secret = "YOUR_SECRET";
// declare the static text that will always be at the start of the response.
string authenticationScheme = "HMACSHA256";
//Get the header payload from the response
AuthenticationHeaderValue authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
// compare the signatures
if ((authHeader != null) && authHeader.Scheme.Equals(authenticationScheme, StringComparison.OrdinalIgnoreCase))
{
string signatureBase64 = authHeader.Parameter;
if (!string.IsNullOrEmpty(signatureBase64))
{
try
{
//Encode the secret
byte[] secretByteArray = Encoding.UTF8.GetBytes(secret);
byte[] content;
// Read request content
using (var ms = new MemoryStream(2048))
{
await Request.Body.CopyToAsync(ms);
content = ms.ToArray();
}
// Generate content hash
using (HMACSHA256 hmac = new HMACSHA256(secretByteArray))
{
byte[] hashBytes = hmac.ComputeHash(content);
string hashBase64 = Convert.ToBase64String(hashBytes);
if (signatureBase64.Equals(hashBase64, StringComparison.Ordinal))
{
return Ok();
}
}
}
catch (Exception)
{
return Unauthorized();
}
}
}
return Unauthorized();
}