Skip to content

Instantly share code, notes, and snippets.

@jakub-fiat-republic
Created October 4, 2022 21:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jakub-fiat-republic/5538482e06352993c90f32d195721c60 to your computer and use it in GitHub Desktop.
Save jakub-fiat-republic/5538482e06352993c90f32d195721c60 to your computer and use it in GitHub Desktop.
node.js webhook signature check
const crypto = require('node:crypto');
;
/**
*
* @param body {string}
* @param secret {string}
* @param signatureInput {string}
*/
const makeSignatureNaive = (body, secret, signatureInput) => {
const digest = crypto.createHash('sha1').update(body).digest('hex');
const signatureParams = signatureInput.replace("fr1=", "")
const signatureBase = `"digest": "${digest}"\n@signature-params: ${signatureParams}`;
const hmac = crypto.createHmac('sha256', secret).update(signatureBase).digest('hex');
return {
"Digest": digest,
"Signature": `fr1=:${hmac}:`,
}
}
/**
*
* @param body {string}
* @param secret {string}
* @param signatureInput {string}
*/
const makeSignature = (body, secret, signatureInput) => {
const digest = crypto.createHash('sha1').update(body).digest('hex');
const signatureParams = parseSignatureInputs(signatureInput).find(({key}) => key === "fr1")?.value;
if (!signatureParams) throw Error("signatureInput is invalid")
const signatureBase = `"digest": "${digest}"\n@signature-params: ${signatureParams}`;
const hmac = crypto.createHmac('sha256', secret).update(signatureBase).digest('hex');
return {
"Digest": digest,
"Signature": `fr1=:${hmac}:`,
}
}
/**
*
* @param signatureInput {string}
* @returns { { key: string, value: string }[] }
*/
const parseSignatureInputs = (signatureInput) => signatureInput.split(',')
.map(input => input.trim().split("="))
.map(([key, ...value]) => ({key, value: value.join("=")}))
// test cases
const testData = [
{
input: ["[]", "123", 1624492800000],
expected: [
"97d170e1550eee4afc0af065b78cda302a97674c",
"fr1=(\"digest\");created=1624492800000",
"fr1=:a8b7794211cff4bdfa1c46aeef3b6345d5029a18a79abd65d584c1f4feaeae3a:"
]
},
{
input: ['{"b":"b","a":"a"}', "ABCDE", 1661385600000],
expected: [
"43bfde7be62c60a94680552060dd68bd65a241ef",
"fr1=(\"digest\");created=1624492800000",
"fr1=:a9819dbe1b4b9dac6f0814179a12e9e0d324e8ce44e3dc4b40d4a0d67cfa1d47:"
]
},
{
input: ['{"a":"a","b":"b"}', "ABCDE", 1640995200000],
expected: [
"3928087092fa7dd910e5d4b2f57765fb2d2aae39",
"fr1=(\"digest\");created=1640995200000",
"fr1=:064dc5771fa3bd8315c1914f328fa0fa9b794acacd35e84237602cd741addd7a:"
]
},
]
testData.forEach(({input, expected}) => {
const [body, secret, created] = input;
const actual = makeSignature(body, secret, `fr1=("digest");created=${created}`)
console.assert(expected[0] === actual.Digest, expected[0], actual.Digest);
console.assert(expected[2] === actual.Signature, expected[2], actual.Signature);
const actualNaive = makeSignatureNaive(body, secret, `fr1=("digest");created=${created}`)
console.assert(expected[0] === actualNaive.Digest, expected[0], actualNaive.Digest);
console.assert(expected[2] === actualNaive.Signature, expected[2], actualNaive.Signature);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment