For our hubspot integeration we make use of webhooks so see when properties change, for the authentication of requests being made from hubspot to our system we are going to use the `X-Hubspot-Signature` headers as explained in the documentation:
We use a python system and I was able to mimic hashing behaviour as used in the example. Because of this I wanted to start testing using `test subscriptions` in hubspot GUI.
What I did is setup a https://putsreq.com bucket to catch my test request and test if the my hashing method of the request results in the same hash als was in the header. Sadly this was not the case.
The core of my question is exactly how does hubspot hash the request before sending it? constructing it exactly like in the documentation does not yield the correct result, here I took the correct secret key, the test url and the request body as formated when using `Copy JSON` button.
Does anybody know what I might be doing wrong?
Below you can find a python code snippet I used to test this. Here the example from the documentation does work, but my test from hubspot itself does not.
from hashlib import sha256
def main():
# example request from documentation:
# https://developers.hubspot.com/docs/faq/validating-requests-from-hubspot
secret = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
url = "https://www.example.com/webhook_uri"
body = '{"example_field":"example_value"}'
xsig = "9569219f8ba981ffa6f6f16aa0f48637d35d728c7e4d93d0d52efaa512af7900"
validate(secret, url, body, xsig)
# Test request made by hubspot from https://app.hubspot.com/developer/5153176/application/189009/webhooks
# The app is only used to debug this exact issue
secret = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy" # need to be replaced with secret from app 189009
# To see requests send here check https://putsreq.com/UKQCqLn1pKDbA1IBpt8S/inspect
url = "https://putsreq.com/UKQCqLn1pKDbA1IBpt8S"
# Direct paste from 'Copy JSON' option in the hubspot webhook overview.
# In our system this data is retreived from this get_data using the `as_text` flag.
# https://tedboy.github.io/flask/generated/generated/flask.Request.get_data.html
body = """[
{
"eventId": 1,
"subscriptionId": 104266,
"portalId": 5153176,
"occurredAt": 1550566240770,
"subscriptionType": "contact.propertyChange",
"attemptNumber": 0,
"objectId": 123,
"changeSource": "CRM",
"propertyName": "email",
"propertyValue": "sample-value",
"appId": 189009
}
]"""
# Signature taken from the X-Hubspot-Signature header in the request send to putsreq
xsig = "237092981e91b0b56714f7c72d42a59736fd99b45f7b22194e5bd84726837136"
validate(secret, url, body, xsig)
def validate(secret: str, url: str, body: str, xsig: str) -> bool:
"""
Compare the hash of the request with the given X-Hubspot-Signature
"""
# build the source of the hash and encode it into bytes so sha256 can hash it.
source_bytes = f"{secret}POST{url}{body}".encode("UTF-8")
local_hash = sha256(source_bytes).hexdigest()
eq = xsig == local_hash
print(
f"Local hash:\t{local_hash}\n"
f"Expected hash:\t{xsig}\n"
f"Equal:\t\t{eq}\n"
f"String before hashing:\n{source_bytes}\n"
)
return eq
if __name__ == "__main__":
main()
Hi @Gijs_vs , happy to help here. Very weird and confusing quirk with HubSpot, but at the top of the article you linked to me, there's a note that says:
Hi @Gijs_vs , happy to help here. Very weird and confusing quirk with HubSpot, but at the top of the article you linked to me, there's a note that says:
Where do you find the "app secret" is this the "client secret"? Also, is there a way to know what the unparsed request body will be? Basically, in the workflow it sends the entire json object for a client, how would our app be able to know what that is to create the hash to compare against?
Hi @bashr , your client ID and secret are found in your HubSpot App in your Developer Account. The payload that's sent through a webhook is the entire contact, company or deal object in JSON format. Your app would need to calculate the hash as the payload is sent to it. In NodeJS, it's something like req.body. One of the headers of that request will be the hash calculated on HubSpot's end, and that's what you'll compare against.