Reputation: 85
I am trying to verify a webhook sent from Plaid's API. Every webhook request is sent with a 'plaid-verification' header which is a JSON Web Token.
The steps required to validate are:
signed_jwt = eyJhbGciOiJFUzI1NiIsImtpZCI6IjZjNTUxNmUxLTkyZGMtNDc5ZS1hOGZmLTVhNTE5OTJlMDAwMSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1OTA4ODcwMDEsInJlcXVlc3RfYm9keV9zaGEyNTYiOiJiNjNhMDdiNTQ3YjAwZjk5MjU0N2Y2YmJjOGQ5YWNjNjFhOGNjZWUxMzhiYzgyZjQ0YTZiYWEwOTY4M2E1ZDBmIn0.OOKvIihgqCj7Qrb2bmz7T3t7uK-0JyjiEqL2s1kWeJBM4MMmjaHKK8GmU_z91QolBWMzvPgs718EElY-rE3cwQ
{
"alg": "ES256",
"kid": "6c5516e1-92dc-479e-a8ff-5a51992e0001",
"typ": "JWT"
}
kid
and POST to /webhook_verification_key/get
POST /webhook_verification_key/get
{
"client_id": "MY_CLIENT_ID"
"secret": "MY_SECRET_ID"
"key_id": "6c5516e1-92dc-479e-a8ff-5a51992e0001"
}
The response is:
{
"key": {
"alg": "ES256",
"created_at": 1560466143,
"crv": "P-256",
"expired_at": null,
"kid": "6c5516e1-92dc-479e-a8ff-5a51992e0001",
"kty": "EC",
"use": "sig",
"x": "35lvC8uz2QrWpQJ3TUH8t9o9DURMp7ydU518RKDl20k",
"y": "I8BuXB2bvxelzJAd7OKhd-ZwjCst05Fx47Mb_0ugros"
},
"request_id": "HvfCtrDLG1ihcp7"
}
key
as a JSON Web Key, validate that the signature of the JSON Web Key is valid, and extract the payload (using jose python library)claims = jwt.decode(signed_jwt, key, algorithms=['ES256'])
claims = {
"iat": 1590887001,
"request_body_sha256": "b63a07b547b00f992547f6bbc8d9acc61a8ccee138bc82f44a6baa09683a5d0f"
}
claims['request_body_sha256']
:Body is in a file body.json
{
"error": null,
"item_id": "yxQbxDjnD8hr69pKbQpbcKeVn3GL9QuyA7NV3",
"new_transactions": 390,
"webhook_code": "HISTORICAL_UPDATE",
"webhook_type": "TRANSACTIONS"
}
Compute SHA-256 of body.json
f = open('body.json')
body = json.load(f)
f.close()
m = hashlib.sha256()
m.update(json.dumps(body).encode())
body_hash = m.hexdigest()
print(body_hash)
body_hash = 'efbb5274864518f7eb3834125d9bcdb95fb03066d3d1bed3ebcc6163d8dc3579'
The body hash in the example above does not equal the body hash received from Plaid. There's 2 possible problems here:
Is there something I'm missing here? Perhaps the request body is encoded differently on my end? I'm using Node.js and Express in production but I made a Python script to follow the method Plaid outlined here, but I'm still not getting the correct hash. I'm honestly out of ideas.
Upvotes: 7
Views: 1732
Reputation: 1853
It seems to be a problem with whitespace. If you modify body.json to have 2 spaces per ‘tab’ on each new line, it will generate the right hash.
Upvotes: 10
Reputation: 101
Alex's answer is right. I ran into the same issue recently, and it does indeed appear that Plaid hashes their body based on a tab spacing of 2. You can use
JSON.stringify(body, null, 2)
to make it work:
const happyBody = JSON.stringify(body, null, 2);
const body_hash = crypto
.createHash("sha256")
.update(happyBody)
.digest("hex");
See also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
Bonus: You could use JSON.stringify(object, replacer, space)
to beautify the JSON. An integer for the last argument will determine how many spaces to use, but you could replace that with something like "/t"
if you wanted tabs instead:
JSON.stringify(someObject, null, "\t")
Upvotes: 10