-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide an example implementation for verifying webhook signatures #8
Comments
Based on the documentation provided, I came up with this implementation in Node.js: exports.validateJaasSignature = (req, secret) => {
const header = req.headers["x-jaas-signature"];
const signatureElements = header.split(",");
let timestamp = null;
let signature = null;
signatureElements.forEach((element) => {
const key = element.substring(0, element.indexOf("="));
const value = element.substring(element.indexOf("=") + 1); // we can't use split as the value can contain (=) characters
switch (key) {
case "t":
timestamp = value;
break;
case "v1":
signature = value;
break;
}
});
if (timestamp === null || signature === null) return false; // signature doesn't match expected format
const hmac = crypto.createHmac("sha256", secret);
const signedPayload = timestamp.concat(".", JSON.stringify(req.body));
hmac.update(signedPayload);
const generatedSignature = hmac.digest("base64");
console.log("Compare JaaS signature:", req.headers["x-jaas-signature"], generatedSignature);
// if comparison checks out
return true;
}; Now, this generates the correct signature MOST of the time, but NOT ALL of the time. I tried to use Any ideas what could cause the mismatch in the signature? |
@mphasize I made it work as expected by replacing We will update the documentation to specify that the hmac message should be the utf-8 encoded byte array of the string. |
@horymury Thank you for looking into it, I will try your solution. |
@horymury so I tried to implement your solution like this:
Both of these generated signatures are the same, but unfortunately they are sometimes (not always) still different from the signature I received in the request header. I also tried a version with I'm also wondering what the TextEncoder would really do, as it is my current understanding that So in the end, I'm still at at a loss.
Yes, the function that calls |
@mphasize utf-8 might be the default so it might not be needed explicitely on new TextEncoder instantiation, but .encode returns an
This is because req.rawBody is not a string, but a byte array I think. Also the encode needs to be done on the whole string which includes the During our testing we noticed that if a webhook is missed which results in re-sending it with the same idempotency key, the body slightly differs the second time but the signature from the header remains the same as the original, which causes the resulting signature to not correspond anymore with the computed signature on the webhook listening endpoint. CC @lstirb8x8 |
@horymury As far as I can tell, the signature mismatch already happens the first time that I see the request in our logs, but I will keep an eye out for this. Since our webhook handler is deployed as a Cloud Function on Firebase, I'm wondering if this might have something to do with a cold-start scenario. Maybe your first request get's a timeout and then already re-tries before I see it. I will go through the logs to see if this might be an explanation. |
The documentation ( https://developer.8x8.com/jaas/docs/webhooks-signatures ) describes how we can check the signature from a webhook call, but a real code example would help with some of the questions we might have after reading the documentation.
For example:
Why one or more signatures? Does another signature always mean another version/protocol or could the message body somehow be broken in several parts with separate signatures?
As the example value contains an equal sign (=) as part of the signature value, I'm wondering how to avoid removing that by accident with a simple String.split...
Since this seems to refer to string concatenation, I'm wondering which specific options to use with JSON.stringify on the request body. Could I break the signature validation by using the wrong stringify settings?
Ummm.. what is constant-time string comparison and how do I do that? 😅
Anyway, a bit of sample code would be really fantastic. Thank you.
The text was updated successfully, but these errors were encountered: