Introduction
As one of the biggest code hosting platforms available today, GitHub provides numerous ways for developers to integrate with the platform. One of those integration options is GitHub webhooks.
In this article, we are going to explore GitHub webhooks. We will look at what a GitHub webhook is, and how to set it up on our GitHub account. Further, we will explore a sample API that receives and logs webhook events. This API will run on our local development environment, thus we will be receiving the webhooks on our local machine. However, if you have a local API you would rather use, feel free to follow along with the article.
What is GitHub Webhooks?
GitHub Webhooks gives developers the ability to integrate with the GitHub platform.
Developers can subscribe to events taking place on GitHub, such as push events that occur when code is pushed to a repository, and respond to the event with an action.
The response action can be anything: making a database entry in an external application, sending an email, or triggering a deployment process, just to name a few. Actions you can perform in response to events on GitHub are limited only by your imagination.
When a subscribed event occurs on GitHub, GitHub triggers an HTTP POST request to the defined destination. This destination is specified as an HTTP URL, which is the endpoint to be called whenever the event occurs. This is known as the webhook URL. The endpoint handler at the webhook URL handles the action to be performed in response to the event.
Along with the request, GitHub also sends the payload of data about the event that triggered the webhook. Information contained in this payload can be used for the action on the endpoint.
Tutorial: How to set up GitHub Webhooks
To begin the tutorial, let's take a look at the steps involved:
- Clone the sample Node.js API for receiving GitHub webhooks on your development machine
- Generate a webhook URL using the Hookdeck CLI
- Register for a webhook on GitHub
- Receive and inspect GitHub webhooks locally
- Make some commits and view logs
Clone a demo API server for receiving GitHub webhooks
As described earlier, GitHub webhooks need to be sent to an endpoint on your API.
If you already have a server running with a POST
endpoint to receive the webhook request, you can skip this section. All you need to do is to take note of the port that your local API is running and also the endpoint to receive webhooks. Substitute these values as you follow this exercise.
If you do not have a local API, you can use a Node.js sample API available on the Hookdeck GitHub repository. Follow the instructions below to set it up.
Clone the repository by running the following command:
git clone --single-branch --branch github-webhooks https://github.com/hookdeck/nodejs-webhook-server-example.git
Navigate to the root of the project and install the required dependencies by running the following commands:
cd nodejs-webhook-server-example
npm install
When the installation completes, you can then run the Node.js server with the following command:
npm start
This will boot up the API application and print a message to the screen indicating that the API is now running and listening for connections on port 1337
.
We are using two endpoints in this project.
-
/log-github-webhook
: This is the endpoint that will be receiving the GitHub webhook and logging it into an in-memory database. It logs a simple object containing a subset of the information from the webhook payload. -
/fetch-webhooks-logs
: This endpoint can be called to retrieve a collection of the logged webhook data.
Get the webhook URL for GitHub
Endpoints on locally running servers cannot receive GitHub webhooks; a publicly accessible endpoint is required and this is where the Hookdeck CLI comes in. The Hookdeck CLI is built specifically for working with webhooks, and helps tunnel external HTTP requests into your local development environment by providing you with a publicly accessible HTTPS URL. You can also inspect the headers and payloads of your webhooks, as you will see later on in this tutorial.
Visit Hookdeck's CLI documentation to install and set up the tool on your operating system. For macOS users, you can run the following command to install the CLI tool:
brew install hookdeck/hookdeck/hookdeck
If you're using the Windows operating system, use the following command to install the CLI tool:
scoop bucket add hookdeck https://github.com/hookdeck/scoop-hookdeck-cli.git
scoop install hookdeck
Once the setup process is complete, the next step is to use the CLI to generate a webhook URL that points to the running API application. To do this, run the following command (note: replace the port 1337
value with the port number on which your API is running if you're not using the sample project):
hookdeck listen 1337
This command starts an interactive session where the CLI collects information about the endpoint you're about to create. Below are the questions and the answers you should supply to each question. Ensure to hit the Enter
key after each answer.
- What source should you select? Ans: select Create new source
- What should your new source label be? Ans: type the text Github
- What path should the webhooks be forwarded to (i.e.: /webhooks)? Ans: type /log-github-webhook (If you're using your own custom server, replace this value with your endpoint)
- What's the connection label (i.e.: My API)? Ans: type My Github Response Server
With this information, the CLI will begin the process of generating the URL and once it's done, you will see the URL printed to the screen and the CLI indicating that it is ready to receive requests.
Note: You will need to use the guest Login URL
link in the console to access the dashboard. Copy and paste this into your browser to begin a guest login session.
Set up webhook on GitHub
Next, you will need to subscribe to a GitHub webhook. Navigate to your repository of choice (this should be a repository you own) and go to Settings → Webhooks. On the Webhooks page, click on the Add webhook
button on the top right-hand corner.
On the webhook form displayed, paste the webhook URL generated by the Hookdeck CLI into the Payload URL
field.
Fill the remaining fields as follows:
-
Content type: Select
application/json
so that you can receive the payload as a JSON object. -
Secret: You can leave this blank, but for the purpose of this tutorial enter
1234ABCD
. -
SSL verification: Leave this as the default option of
Enable SSL verification
. -
Which events would you like to trigger this webhook? This is the point where you subscribe for a GitHub event on your repository. For this tutorial, select the
Just the push event
option, as we are only subscribing to thepush
event. You can either subscribe for all events or a subset of the events using the other two options. - Active: Leave this checked to receive event details when the GitHub webhook is triggered.
See the complete webhook form below:
Click the Add webhook
button to complete the process.
With this setup, any time you push code to your repository, a webhook request will be fired to the specified webhook URL.
Receive and inspect GitHub webhooks and payload
Immediately after you complete registration for a webhook in the step above, a ping
webhook request will be fired to your webhook URL. Because we are using the Hookdeck CLI, we would already see the ping
webhook request logged on the terminal where the Hookdeck CLI is running, as shown below:
Here we can see that we have received a POST
request with a 201
status code indicating that a new entity was created on our server; in other words, a webhook object was logged.
To view details of the webhook request, its headers, and payload, copy the Hookdeck dashboard URL printed along with your webhook request details on the CLI and load it in your browser.
You will see a page similar to the one below:
On this page, you can go through and inspect all the data that came with your webhook request. For example, the Headers
section is expanded in the image below:
You can also view all that is contained in the webhook request payload by expanding the Body
section as shown below:
With this setup, you get full and clear visibility into your webhook requests and you will be able to monitor and quickly troubleshoot your webhooks when any failure occurs.
View Application webhook logs
To check that the webhook information is being logged, make a few commits to your GitHub project. You should see webhook event entries in the terminal where your Hookdeck CLI session is running. Now visit the endpoint /fetch-webhooks-logs
, where you should see a couple of log entries similar to the image below:
As seen above, we are logging the repo
, author
, and timestamp
of the commit. Ignore the first entry as this is simply a test entry so that the application database does not start empty.
GitHub webhooks verification
A publicly accessible endpoint, just like the one being used as your webhook URL, can receive requests from anyone. This situation makes your endpoint vulnerable, as ill-intentioned individuals or bots can easily spoof requests imitating GitHub to dump irrelevant data into your system or manipulate your application.
You should verify the requests hitting your webhook URL to ascertain that they are from GitHub. GitHub helps you achieve this using the token secret we set in the form in an earlier step.
GitHub uses this token to create a hash signature with each payload. This hash signature is included with the headers of each request as X-Hub-Signature-256
.
With this signature, you can validate your payloads. GitHub uses the HMAC algorithm to compute the hash and it is the same algorithm you will use to implement the validation on your server.
At the moment, our project is not enforcing this check but the code can be found commented out as shown below:
//Validate payload
/* function validatePayload(req, res, next) {
if(req.method == "POST"){
if (!req.rawBody) {
return next('Request body empty')
}
const sig = Buffer.from(req.get(sigHeaderName) || '', 'utf8')
const hmac = crypto.createHmac(sigHashAlg, secret)
const digest = Buffer.from(sigHashAlg + '=' + hmac.update(req.rawBody).digest('hex'), 'utf8');
if (sig.length !== digest.length || !crypto.timingSafeEqual(digest, sig)) {
return next(`Request body digest (${digest}) did not match ${sigHeaderName} (${sig})`)
}
}
return next()
}
app.use(validatePayload); */
From lines 10 to 12, you can see the validation variables defined, as shown below:
const sigHeaderName = 'X-Hub-Signature-256';
const sigHashAlg = 'sha256';
const secret = "1234ABCD";
The secret
variable is the key we set when creating our webhook. In a production environment, these values should be kept in environment variables.
You can uncomment the payload validation middleware to ensure that your webhook payload is validated against the API secret set.
For more information on securing your GitHub webhooks, you can check out the GitHub documentation here.
How do I disable a GitHub Webhook?
At the moment, there is no setting on a GitHub webhook configuration that disables a webhook. Some will suggest that you uncheck the Active
parameter but this only stops the delivery of the webhook payload, it may not prevent the event itself from being fired.
The only way you can be sure you have disabled a webhook is by deleting it completely from your GitHub Webhooks page. This may seem a bit extreme if you only want to disable it temporarily.
With Hookdeck, you can use the Pause
feature on the dashboard to temporarily stop your webhooks from being delivered. When you're ready to start receiving them once again, you can unpause the webhook.
Conclusion
Webhooks are a fantastic offering by GitHub to help developers extend their integrations with the platform to more custom use cases. This enables the development of more robust applications that take advantage of events occurring on GitHub.
In this tutorial, you have learned how to set up and receive GitHub webhooks on your API endpoints. The tasks you can perform in response to these events are only limited by your imagination.
Happy coding!
Top comments (0)