Webhook requires HMAC-SHA256 signature for verification

Hello Bubblers

I’m trying to set up some webhooks, but they are proving rather tricky as they need a HMAC-SHA256 signature to verify them. The docs say:

“A HMAC-SHA256 signature will be sent based on the shared secret and the request body. That signature will then be placed in a X-Hook-Signature header. The endpoint can then verify the signature to know if the message is authentic. Verification is as simple as computing an HMAC-SHA256 signature using the shared secret as the key and the request body as the message, and comparing it to the X-Hook-Signature header.”

I’ve had a look through the forum but have only come across this which encrypts rather than decrypts:

Does anyone have any ideas or pointers in the right direction?

really appreciated! Thanks

You can’t ‘decrypt’ a HMAC-SHa256 signature… HMAC-SHa256 is a hashing function, not an encryption function, so it can’t be decrypted (it’s a one-way process)…

What you need to do is compute your own version of the signature (the epxected signature), as per the Webhook Docs, and then compare your version to the version sent in the X-Hook-Signature Header.

If they match then you know it’s a genuine request…

4 Likes

I see… thanks for the clarification @adamhholmes seems a bit clearer now, although I’m a bit clueless as to how i need to do this on the api workflow, once the webhook is received, does it require something like Miguel mentions here with the toolbox plugin?

Thanks a lot for taking the time, appreciate it :pray:

Nope, no plugins or custom code required, you can do it all within Bubble’s built in functionality…

You need to construct the signature based on the Webhook’s request data, as per the Webhook documentation.

Annoyingly, you can’t access the raw request data from a webhook in Bubble, so you have to request it manually (which is a bit redundant, and shouldn’t be necessary, but it’s just the way it is).

So you need to set up an API call to retrieve the event, and set the API datatype to text.

Then, when the webhook is received, the first thing you need to do is retrieve the request in its raw text data using that call - again, this does seem a bit stupid, as the webhook is sending you the data in the request body, so its seems redundant having to request it again… but it’s just the way you have to do it in bubble (at least for now), and it shouldn’t really matter anyway.

Then you need to construct the signature using the data in the event you’ve retrieved, as per the Webhook docs. You can use Arbitrary texts for this, then format as HMAC-SHa256.

Then just compare hashed version of the signature you constructed to the one sent in the request header using a conditional.

If Bubble let us access the request data as raw data directly in the backend workflow, then we wouldn’t need to make the extra API call to get the raw request body.

6 Likes

Allrighty… rolling up my sleeves to try tackle this beast… :muscle: Seems like a dizzying amount of work for a simple webhook… if only they were all easy like stripe! Really appreciate your input, thanks a lot man. I’ll update on progress, once i’ve pieced this all together. Cheers

I seem to be stuck between a rock and a seemingly bigger rock trying to set up these webhooks… Any input hugely appreciated :pray: @adamhholmes hope you don’t mind me tagging you, I ran into this trying to accomplish what you suggested (and still didn’t manage to achieve🥵), details of the state of that included in Hurdle 2 if you have any additional pointers

In order to set up a webhook, I’m required to send a POST request as follows:

As this is done, the service fires back a POST request to the bubble /initialize URL including a webhook ID and a x-hook-secret - this works fine

Then in order for the webhook to work, it needs to be activated by sending PUT request along with the webhook ID and a x-hook-secret as follows - this works fine too:

Hurdle #1: In order to get any actual webhooks to fire to this, i need to remove the /initialize from the URL, however the service then sends a NEW x-hook-secret (because the URL has changed - " A POST request will be made immediately after a webhook is setup, or whenever the URL changes. This request will have a unique secret in a X-Hook-Secret header along with the id of the webhook that was just created.") - and I can’t access it because Bubble needs the /initialize in order for me to detect it…

I tried these step sending the requests to webhook.site and the webhook fine. It’s just proving very tricky in Bubble:

Hurdle #2: Related to my original post, trying to execute your steps Adam. “So you need to set up an API call to retrieve the event, and set the API datatype to text.” This seems to initialize fine:

Then "first thing you need to do is retrieve the request in its raw text data using that call " so on the backend workflow when the webhook is meant to fire, I set up a call to retrieve the request and compare it to the expected payload of the webhook as per the docs. Included the payload (which i got from the test on webhook.site) in arbitrary text formatted as HMAC SHA256, but then there isn’t a X-Hook-Signature to compare to in the expression…

I also tried a simple version of trying to just create a new record in the database with the webhook_id to see if a webhook is being received, but i’m unsure if it is because of the /initialize catch

Yelp! Thanks to anyone who’s read this far!

2 Likes

Did you figure this out? When I do formatted as HMAC SHA256 it’s asking for a HMAC secret, and if the HMAC secret is the key that we retrieved, then what is the conditional after that to compare it to?
EDIT: I missed the part about running a separate call to get the raw request data, testing that out before I come back for more help…

In the backend workflows, make a workflow that will make the HMAC. Use Arbitrary text (will contain the text to be encoded) and then use “format text as:” and select HMAC-SHA256 save it to user details and delete after comparing to the code that was sent.


1 Like

The HMAC secret is your Webhook’s signing secret (as accessed from your Stripe dashboard)…

1 Like

@tyrelsmythe Any luck in solving this one? were you able to send the x-hook-secret parameter in the header for the handshake without a custom code or custom plugin?
I came across this thread as well that have similar request Trigger API Workflows with GET Requests - #38 by salama

@basel.3adel unfortunately not… ended up using Make (integromat) after days of back and forth… works really well and easy to set up. I see they have an Asana integration too. Thanks for sharing the other thread, will keep an eye on it for any further developments. Good luck solving your problem!

We have built custom Cloudflare Workers to handle and authenticate webhooks for Shopify. I’m very certain you can adopt this to any webhook

Reach out as it requires custom setup

Hey Alifarahat,

We are implementing Stripe integration and trying to verify signature of Stripe. We put in some code to calculate the SHA256 and verify it with the signature returned in Stripe’s header but we can never get it working. Can you help?

For anyone with this issue I recommend checking out Hookdeck.com which can take care of the webhook verification for you, then forward the requests on to Bubble.

It has in plugins for Shopify/Stripe and others, I’ve been using it a few months and it solved the problem perfectly for me.

2 Likes

I do not understand that. If you are using a middleman, how do you verify the signature of the middleman?

Hi,

I am using docusign and this is what I have done for the webhook in my bubble app to get back some info.
I add a condition in my API workflow (webhook)
image
the arbitrary text is the raw text from docusign
Then I use the bubble function “formatted as HMACSHA256” on the text
And I compare the result with the signature contained in the header.

I hope it will help you

3 Likes

I have a similar problem with the Duo API as it also requires the signature to be in HMAC format. From the Duo documentation: Generate the HTTP Password as an HMAC signature of the request. This will be different for each request and must be re-generated each time.

To be honest, I am a little lost here as to how to set up this API call. Does anybody have a working Duo API they wouldn’t mind sharing with me?

1 Like

:rofl: Have been asking the same thing - why not just send the data in the response?

1 Like

Yeah, I’ve been onto Bubble support regarding this for about 6 months… they’ve added it to their feature requests lists, but I’m not holding my breath. (they did introduced the option to get the raw body text from the API request, but it removes all spaces, line-breaks, and indents, so cannot be used to validate webhooks in most cases).

FYI The answer I gave in this thread back in August, was in specific reference to validating webhooks from Stripe (even if the original post wasn’t) and, in case you’re not aware, the method of retrieving the raw body I mentioned no longer works for validating webhooks from Stripe, as they have changed their event API payload since then - so the raw body of the original webhook request and the body of the event retrieved by the API call are no longer identical (as the payload now contains a reference to the number of pending webhooks).

Actually looking at Shopify and their webhooks to pull order data into Bubble. It’s not particularly different. The endpoint is created in the partner portal but they recommend you verify the webhook is from Shopify using a POST with the client secret from the app dashboard. So you kind of hit it twice to get what you want during the initial set up.

X-Shopify-Topic: orders/create
X-Shopify-Hmac-Sha256: XWmrwMey6OsLMeiZKwP4FppHH3cmAiiJJAweH5Jo4bM=
X-Shopify-Shop-Domain: {shop}.myshopify.com
X-Shopify-API-Version: 2023-04
X-Shopify-Webhook-Id: b54557e4-bdd9-4b37-8a5f-bf7d70bcd043
X-Shopify-Triggered-At: 2023-03-29T18:00:27.877041743Z

Definitely a bit more work in setting up webhooks on their platform.