Trigger events with Kinde webhooks

By Vlad Ionash — Published

Kinde’s webhooks feature is live, a significant development for developers and businesses integrating authentication services into their applications.

Webhooks are user-defined HTTP callbacks that are triggered by specific events.

You can trigger a custom action flow in a third party service when certain events take place, such as a registration or profile update.

For instance, sending a custom email response to specific users based on certain criteria during sign-up, or notifying a discord server with a message when a new user is authenticated.

Webhooks enhance your capacity to create more dynamic, responsive, and personalized user experiences.

To showcase the feature, we’ll be building an example where we can track how many users have authenticated in a Discord Server!

  • Kinde Application that you can log-in/sign-up with
  • Zapier (You can use other services like Activepieces or any Webhook supported app)
  • Discord bot (This tutorial will utilize Zapier’s own discord bot service which you can add to your server instance separately)

We are going to assume that you have a working application that uses Kinde’s auth. If you do not, you can reference and utilize one of the Kinde Starter Kits.

  1. Navigate to SettingsWebhooks on the sidebar.

  2. Click on Add Webhook.

  3. Setup the Webhook.

    Here we will define the information that we need. Add any Name and Description that will help you differentiate this from other ones that you have setup.
    The Endpoint URL will be defined with the application that you want to integrate with. A good way to test this out would be to use something like https:/ webhook.site/. They will provide you a URL that you can hit which you can see the body response to, to see if it is working.
    Events can be configured as needed. For this, I’ll be adding all the events.

    ⚠️ Be wary of limitations on external providers. A lot of third-party integration services are restricted with how many queries they can parse or charge you per instance that the Webhook is hit. If you are restricted by this, make sure to only toggle the events that are needed to avoid charges
    After this is configured, let’s test with https://webhook.site/. Go to an application that is tied to your Organization that the Webhook is setup with, and perform an event action such as a sign-up or sign-in.

    Your response should look something like the above.

    Notice how the response looks something like a string of random characters in the body:

    eyJhbGciOiJSUzI1NiIsImtpZCI6IjRkOjk1OjVkOmNiOjZiOjRkOjVmOmQ0OmIyOjBhOmZhOjlmOjVkOmJlOmNhOjlkIiwidHlwIjoiSldUIn0.eyJkYXRhIjp7InVzZXIiOnsiaWQiOiJrcF9jM2Y3Y2U4NzY0YWM0YjFlYWVkZjQzNWNlMzhjMWQwMSJ9fSwiZXZlbnRfaWQiOiJldmVudF8wMThlN2Q1YzMyZDQ4MzZkZWRmNGIwOWNhYTA0YzRjNiIsInNvdXJjZSI6InVzZXIiLCJ0aW1lc3RhbXAiOiIyMDI0LTAzLTI2VDE3OjQyOjU4LjA4OTM1My0wNzowMCIsInR5cGUiOiJ1c2VyLmF1dGhlbnRpY2F0ZWQifQ.Y2LlMQ6M_rUQVLujjp1HT45VRCkwTOuAelg0CwZNAsPx4oGtKbb83w61rt8Q1xqDrxS3GdQqt-fSQ8RbVK3L9TW5ck4qDKe9gmvQ-v3uFVh-j04X3ihDjMC3xizgBjNA6y5KhqaB_xiY-MiHDpBYLA2675-YeD7x1Fynd58xG8ArhyydoB7C6WzR80YoPT4hbUCBjfK2tJLctH9dymqvDPpSFZNx6RFBYb6gm31TCLFdAZgtsdUsq2fYpLE8z9-D80LEHW9PZAGyzvLMynuAseRWQLff6FqTMHuEal_JZKDBluYpd54vdxhFlzz3Oij4PUeP2Oq9wPYXe1gSefiTcw

    This is because it’s encoded as a JWT. We will need to decode this to get the information that we want.

    Something simple that can accomplish this in JS would be:

    const jwt = inputData.jwt; // Assuming inputData.jwt is your JWT
    const base64Url = jwt.split(".")[1]; // Get the payload part of the token
    const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
    const payload = JSON.parse(Buffer.from(base64, "base64").toString("ascii"));
    
    output = {decodedPayload: payload};

    or you can utilize an online tool here: https://kinde.com/tools/online-jwt-decoder/

    The above gives me the header response of:

    {
        "alg": "RS256",
        "kid": "4d:95:5d:cb:6b:4d:5f:d4:b2:0a:fa:9f:5d:be:ca:9d",
        "typ": "JWT"
    }

    With the payload being:

    {
        "data": {
            "user": {
                "id": "kp_c3f7ce8764ac4b1eaedf435ce38c1d01"
            }
        },
        "event_id": "event_018e7d5c32d4836dedf4b09caa04c4c6",
        "source": "user",
        "timestamp": "2024-03-26T17:42:58.089353-07:00",
        "type": "user.authenticated"
    }

    We will be using the “type” parameter here for our example app to send a Discord message whenever someone is authenticated!

For this example, we will be using Zapier to integrate our webhook instance. Be aware you are able to utilize webhooks in a variety of different applications or you can even build your own flows by hosting servers or endpoints that can be hit publicly, such as ngrok instances.

  1. Create a new blank Zap.
  2. On the trigger menu, select Webhooks by Zapier and configure it to catch the raw webhook.
  1. You will be provided a webhook URL which you will need to replace with the one from Step 3 in the Kinde Setup.
  1. Perform an event that you are listening to. In my case, I will sign-in my authenticated application. After the event, press Test trigger.
  2. Select the event from the menu. Notice the Raw Body containing the JWT.
  1. Select Continue with selected record.

  2. Add another action called Run Javascript in Code by Zapier.

    We need to write some code to parse the contents of that body. We already wrote this code initially in Step 3 for the Kinde Setup, so we can just copy and paste it into the code request.

    const jwt = inputData.jwt; // Assuming inputData.jwt is your JWT
    const base64Url = jwt.split(".")[1]; // Get the payload part of the token
    const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
    const payload = JSON.parse(Buffer.from(base64, "base64").toString("ascii"));
    
    output = {decodedPayload: payload};

    In the input field, make sure to assign the variable “jwt” to the Raw Body response from the webhook. which you can elect from the dropdown. The end result should look like this:

    We can test it, and the response should look familiar:

    ⚠️ For cases where this information is strictly confidential or vital, you should utilize the incorporation of your public jws key to verify the signatures that they are indeed being sent by the correct person. Otherwise you could be susceptible to an attack of some kind. Your public key can be found at https://<YOUR_DOMAIN>.kinde.com/.well-known/jwks

  3. Add a path rule which aligns with the event handling that you want. For simplicity, I set mine to match exactly with the output of what I’m looking for: “user.authenticated”

  1. Add the trigger that you want to happen. I’ll add a simple Discord integration that Sends a Channel Message in Discord whenever my event matches what we have constructed above with the text “Congrats! Another user authenticated!“.

And there you have it! We constructed a full flow that utilizes Zapier as the webhook processing agent. Now when a user authenticates in our App, we will get a Discord message in the server of our choice of the action with our custom message:

This is all fully customizable and can be set to be as unique or as complicated as you wish.