Webhook Signature + NodeJS - Possible Unicode Escape Issue
Opened this issue · 2 comments
Spent the last several hours trying to figure out how we can achieve the same type of encoding as the examples that were provided if there are unicode present in the payload but I can't seem to get it to work.
import { createHmac } from 'crypto';
export const verifyGreenhouseWebhook = (req: https.Request) => {
const { body } = req;
const signature = (req.headers['signature'] as string).split(' ')[1];
const localSignature = createHmac('sha256', process.env.secret as string)
.update(JSON.stringify(body))
.digest('hex');
return signature === localSignature;
};
This is what I have written to verify the webhooks signature and the only time it ever works is when I make an update to the webhook, and it sends a "ping" to my API. I am able to generate the exact same signature since the body of the request is quite minimal. However, when a webhook trigger for say contact/prospect is made and the body of the request is quite extensive, it does not generate the same signature.
Things I've tried:
- encodingURIComponent around the string body
- decodeURIComponent around the string body
- Attempted to use the rawBody buffer value
- Removed the
organization_name
andorganization_id
from the root of the payload since the documentation only says that it receivesactions
andpayload
Does anyone have any idea how we can achieve the signature matching on NodeJS or at least point me in the right direct?
Hey @asithade just in case you're still stuck on this (or anyone else finds this) we had the same issue. We contacted Greenhouse support and they weren't able to give a list of which unicode characters were escaped or why, however in the end we figured out that we could get around this by calculating the hmac directly from the buffer representation of the request body. It's really important that it's a buffer and is not converted to a string at any point. The reason for that is because when javascript constructs the string, it parses unicode escape sequences into their corresponding characters. Thus in node we have
'\u003cp\u003ehello\u003c/p\u003e' === '<p>hello</p>' // strict equality check returns true
Whereas if we tried constructing a hmac from the two sides as buffers (e.g save each strings to a file and use fs.readFile()
) you'll see that the buffers are very different and the resulting hash differs.
@silawrenc @asithade I tried using the rawBody buffer value doing something like:
app.use(
bodyParser.json({
limit: MAX_INCOMING_PAYLOAD_SIZE,
verify: (req, res, buf) => {
Object.assign(req, { rawBody: buf });
},
}),
);
then
.update(req.rawBody)
but it still didn't work. Any pointers?