It again produces a different hash than what's in the request header x-hubspot-signature-v3.
The documentation isn't explicit about what variant of Base64 encoding should be used (url-safe or not; with or without padding - it's with padding based on the actual value I got in the request header) but I tried all of them and neither worked.
I also noticed that the new docs for version 2 signature don't say how the result should be encoded, but I assume it's as before, that is Hex encoding.
Workflow webhook signature validation - sha256 produced by my code doesn't match the request header
SOLVE
I had a feeling after reading your original post the first time that something was off about that secret key. That's a private app key! "pat-na1" stands for Private App Token in North America. That would explain why even after all of that, it still is incorrect.
The secret keys the documentation is referring to are developer apps where you are given a client ID and client secret key. Developer app secrets are in the UUID format. Here's a link to how to create a developer app:
Workflow webhook signature validation - sha256 produced by my code doesn't match the request header
SOLVE
Hi, @JurajMartinka👋 Thanks for reaching out. Let's see if we can get the conversation going for you — hey @nikodev@JBeatty@tominal, do you have any experience here? Thank you for taking a look! — Jaycee
I can tell you I spent days trying to get PHP to match HubSpot's signature. I'll paste my v3 code here to break it down. Hopefully other PHP developers will roll across this post and save them time. It's not fun.
$timestamp = $request->headers->get('X-HubSpot-Request-Timestamp');
$hash = hash_hmac('sha256', 'GEThttps://yourdomain.com' . $request->getRequestUri() . (strlen($request->getContent()) > 0 ? $request->getContent() : '') . $timestamp, {your_client_secret}, true);
// Reject the timestamp if older than a few minutes
if(!hash_equals($request->headers->get('X-HubSpot-Signature-v3'), base64_encode($hash))) // Error out if the hashes don't match
Edited 04/13/23: Syntax fix for easy copy-paste
Breakdown:
HTTPS your domain
Make sure that the body is just the body of the request
Concat the timestamp from the headers
Pass your client secret as the key of your hash function
The hash function has to spit out the raw binary output of the hash otherwise it will output lowercase hexits
Base 64 encode that puke
This will be equivalent to the X-HubSpot-Signature-v3 header.
If I had more time I would convert that to a Python version.
The actual hostname is different but I tried your PHP code and it produces the same signature as my code, that is this value in my case (with proper hostname):
R9Eb1i4n/vzEollD9qFu6h+LDMSbMAN0ckn8RJHiAJc=
Yet the value in the x-hubspot-signature-v3 header is completely different, that is EmURxhKIXoSDml+j/GSmf5jUrfLckNQatujkHhwHMpw=.
As a last resort, I tried to hardcode the request method to "GET" (although that's obviously incorrect in my case) but it doesn't match either.
Now I'm stuck - there might be some little detail, but I don't really know what's going on :(.
Workflow webhook signature validation - sha256 produced by my code doesn't match the request header
SOLVE
I had a feeling after reading your original post the first time that something was off about that secret key. That's a private app key! "pat-na1" stands for Private App Token in North America. That would explain why even after all of that, it still is incorrect.
The secret keys the documentation is referring to are developer apps where you are given a client ID and client secret key. Developer app secrets are in the UUID format. Here's a link to how to create a developer app: