Step by Step Webhook Tutorial
The best way to understand how to use webhooks is to practice doing one.
This tutorial is for client developers and is a step by step guide in how to create and use a webhook in Bracken.
In this tutorial you will create a public endpoint using Ngrok, and go through the authorisation process to get an access token to be able to subscribe to a webhook, then observe the response and authenticate the payload.
This tutorial was created using the editor Visual Studio Code, Ngrok software, Python 3.6.8 and Node.js
Prerequisites
- Familiarity with the command line (only for the purpose of this tutorial)
- NPM is installed (only for the purpose of this tutorial)
- Already have a Bracken
domainkey
,client_id
andclient_secret
Create a new project
Create a new project folder called ‘Bracken-webhooks-test’ and move into that directory. This folder will contain your Ngrok file and Node.js project. The end product of this will be a public endpoint which the webhook payload will be sent to when the event is triggered.
Note We will be triggering the webhook event to understand what they payload will look like.
In the example code this project folder is called “Bracken-Webhooks-Test”
Create an Express app
- Open your preferred editor and move into the directory that we have just created.
- Open a new terminal and run
npm init
. - Accept the default values that are created in the package.json file.
Your express app should look like this with package.json
, package-lock.json
, > node_modules
inside.
install Express by running
npm install express
in your terminal.Create a new file called
index.js
In this file we are going to create an instance of express and create endpoints to respond to the incoming requests.Within
index.js
add the following code. The comments explain what each part does.
// Enable express to be used
const express = require('express');
// app is an instance of express.
const app = express();
// app.METHOD(PATH, HANDLER)
app.post('/webhooks', function (req, res) {
// This will return a 200 with the message 'OK'
res.send('OK')
});
// Respond to incoming get requests with ‘hello’:
app.get('/', (req, res) => {
res.send('Hello!');
});
// The app is listening to requests on port 5000
app.listen(5000, function () {
console.log('Listening for webhooks on port 5000')
})
Getting a public endpoint
For this tutorial we are using ngrok for a public endpoint.
If you already have a public endpoint available then you can skip the ngrok section of this tutorial and host the index.js
on your own public endpoint and then when creating the webhook, use your public url as the target
.
Install Ngrok
Ngrok is a software that creates a public url and therefore a public endpoint which is where our webhook payload will be sent to.
Note You can choose to create a Ngrok account although it is not needed for this tutorial.
- Go to https://ngrok.com/
- Click ‘Getting started for free’
- Download the correct Ngrok version for your operating system.
Unzip to install
On Linux or Mac OS X you can unzip ngrok from a terminal with the following command.
unzip /path/to/ngrok.zip
On Windows, just double click ngrok.zip to extract it.
- After unzipping the file, either drag the
Ngrok.exe
file into your project folder or use the terminal to move theNgrok.exe
file into your project folder.
Current Project:
The current project should now be one folder containing package.json
, package-lock.json
, > node_modules
, index.js
and ngrok.exe
Run project
- Save all changes you have made.
- Open a terminal window.
- Run
index.js
with the commandnode index.js
in the terminal window - Open a new terminal window.
- Start up Ngrok with the command
./ngrok http 5000
This will connect Ngrok with your localhost:5000 and the URL that Ngrok supplies will be the same as what appears on your localhost:5000
Trouble Shooting
If your Ngrok URL and localhost:5000 do not match then stop running Ngrok and rerun it using this command instead ./ngrok http 127.0.0.1:5000
Current Project:
You now have both your index.js file and Ngrok currently running
Create a Webhook
Now we are going to create a webhook and this means we are going to subscribe to one.
In this example we are going to sign up for the User_Create webhook.
Create a new project folder
In the example code this project is called “Control-Trigger” that is because this project folder will be used to create a webhook and trigger it.
Move to the “Control-Trigger” folder in your directory via your preferred editor.
Webhook User_Create
Create a new file called webhook-User_Create.py
In this file we are going to get an access token and sign up for the webhook that will get triggered when a new user is created.
Copy the Ngrok HTTPs URL. This is a public endpoint that can be accessed anywhere. This is going to be your target
that you will create the webhook with.
Warning
Every time you run Ngrok you will have a different URL. Do not close Ngrok for this next part otherwise the target
you give the webhook to send responses to will never see a response.
Create python file to create webhook
In the file webhook-User_Create.py
add the following code but read the next section before running the file.
# Creating a webhook, triggers when new users are added.
import requests
from requests_oauthlib import OAuth2Session
from oauthlib.oauth2 import BackendApplicationClient
# declare your own values here.
# domainkey is an integer
domainkey = YOUR DOMAIN KEY
client_id = 'YOUR CLIENT_ID'
client_secret = 'YOUR CLIENT_SECRET'
# Identity Server URL
token_url = 'https://identity.brackenlearning.com/connect/token'
# use your information to get authorization to access the API
client = BackendApplicationClient(client_id=client_id)
oauth = OAuth2Session(client=client)
token = oauth.fetch_token(token_url=token_url, client_id=client_id,client_secret=client_secret)
#request an access token
s = requests.Session()
s.headers.update({'Authorization': 'Bearer {}'.format(token['access_token'])})
# declare the webhook you wish to sign up to
urlMerge_Webhook = 'https://api.brackenlearning.com/api/webhook/merge/domain/{0}/event/user_create'.format(domainkey)
# create the webhook and call it.
response = s.put(urlMerge_Webhook, json={"target":"https://224438f19e12.ngrok.io/webhooks", "secret":"12345", "active": True})
print(response)
print(response.json())
Note:
It is strongly advised to have a secure secret when creating any webhook.
Please change the secret if you are going to use this code.
Add your own
domainkey
,client_id
andclient_secret
values.Change the target to your Ngrok generates URL. Do not forget to add /webhooks to the end the URL.
Open a new tab in your preferred web browser
Copy this address into the URL address bar http://127.0.0.1:4040. This is a Web Interface generated by Ngrok and it is running on your local machine. This will enable us to see the responses generated.
Run the python file
webhook-User_Create.py
You should see a [200] response and the JSON payload that you have sent to Bracken.
Trigger Webhook
Now we want to trigger the webhook we have just created by triggering the event User_create. We can do this by creating a new user.
Create New User via API
We are going to create a new user via the API.
This will give us practice with interacting with the API and the project file name is called “Control-Trigger”. We have done the control part by creating a webhook and controlling where the payload will be sent and now we need to trigger the webhook to confirm we have done it right.
Create python file to trigger webhook
- Create a new python file and call it
newUser.py
- Add the following code to the file
# Creating a webhook, triggers when new users are added.
import requests
from requests_oauthlib import OAuth2Session
from oauthlib.oauth2 import BackendApplicationClient
# declare your own values here.
# domainkey is an integer
domainkey = YOUR DOMAIN KEY
client_id = 'YOUR CLIENT_ID'
client_secret = 'YOUR CLIENT_SECRET'
# Identity Server URL
token_url = 'https://identity.brackenlearning.com/connect/token'
# use your information to get authorization to access the API
client = BackendApplicationClient(client_id=client_id)
oauth = OAuth2Session(client=client)
token = oauth.fetch_token(token_url=token_url, client_id=client_id,client_secret=client_secret)
#request an access token
s = requests.Session()
s.headers.update({'Authorization': 'Bearer {}'.format(token['access_token'])})
# URL to create a new user
urlCreateUser = 'https://api.brackenlearning.com/api/user/create/domain/{0}'.format(domainkey)
# Send a POST to create a new user in your domain
response = s.post(urlCreateUser, json={"username":"Test-User", "password":"JSue5be8wuI8kei328Jdu0ew*3js"})
print(response)
print(response.json())
You can change the JSON body to your preferred username and password.
WARNING
It is strongly advised when creating a new user to create a strong password.
Change the values to interact with your own domain. Run the python file. The output will be a new test user.
Confirm Webhook was Triggered
We have now created a user and in doing this we should have triggered the User_Create event, which triggers the webhook we have just created.
Open the Ngrok Web Interface tab in your web browser that we opened earlier on. You should see a POST/webhooks 200 OK in the requests panel.
If you click on this you will be able to see the the payload of the Webhook and we can confirm that a new user was created called “Test-User” ha been created.
Authenticate Webhook
Now that we have the received the payload we need to confirm that it came from Bracken. The Ngrok URL is a public endpoint which means that anyone on the internet can send information to the endpoint. We can use the header of the payload to confirm that it came from Bracken by encrypting the secret that we created with the body of the payload. By encrypting this we can then match it to the already encrypted header of the payload and if the two match it means that 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.
- Go to the “Bracken-Webhook-Test” folder and open
index.js
file. We are going to modify this file so that when the webhook response is received, it is instantly authenticated and we will only receive a [200] response if the webhook came from Bracken. - Add the following code to
index.js
. The un-commented code is what you should add.
// 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 ";
// when there's a post request to /webhooks
//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');
}
});
// respond to incoming get requests with ‘hello’:
// app.get('/', (req, res) => {
// res.send('Hello! wtf are you connected to!?');
// });
// app.listen(5000, function () {
// console.log('Listening for webhooks on port 5000')
// })
Authentication code explanation
By adding this code to index.js
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 can trust the response and then react to the data how you see fit.
Clean up
In the source code provided there is a python file called deleteUser.py
you can use this to delete the ‘Test-User’ you have created during this tutorial. This code is identical to the newUser.py
file except the url has changed so it will delete a user instead.
When you wish to create a webhook on your own system you can simply run the webhook again but this time change the target
to your public endpoint
Thank you for reading this step by step tutorial on Bracken Webhooks.