I am working with Nodejs and I want invoice to status to updated by net.authorize.payment.authcapture.created event. Payment is successful but the webhook signature I get Invalid HMAC signature. HMAC do not match
[0] Webhook Request Headers: {
[0] host: 'f273-97-64-131-130.ngrok-free.app',
[0] 'content-length': '438',
[0] 'content-type': 'application/json',
[0] expect: '100-continue',
[0] 'x-anet-signature': 'sha512=72A339F0FA63917346CAD6C5EFD84F20321BB1177459024C2A56B9602D1573D66BD27BEE09858843FEA80885C6BE3F0AA2A5A3C866D9261FE72774F87BCD7031',
[0] 'x-forwarded-for': '198.241.206.38',
[0] 'x-forwarded-host': 'f273-97-64-131-130.ngrok-free.app',
[0] 'x-forwarded-proto': 'https',
[0] 'x-opnet-transaction-trace': '9903cec4-6192-4042-9bce-b5b44bd782f2-11088-50965',
[0] 'accept-encoding': 'gzip'
[0] }
[0] Payload for HMAC: {"notificationId":"43396377-04b6-4c52-bb7f-5209d6376daa","eventType":"net.authorize.payment.authcapture.created","eventDate":"2025-02-06T04:38:23.3448159Z","webhookId":"d516f763-b101-459d-abed-3acbd836965a","payload":{"responseCode":1,"authCode":"MQ3APZ","avsResponse":"Y","authAmount":4000,"fraudList":[{"fraudFilter":"Amount Filter","fraudAction":"report"}],"invoiceNumber":"2025-0030","entityName":"transaction","id":"80037536431"}}
[0] Received HMAC: 72A339F0FA63917346CAD6C5EFD84F20321BB1177459024C2A56B9602D1573D66BD27BEE09858843FEA80885C6BE3F0AA2A5A3C866D9261FE72774F87BCD7031
[0] Computed HMAC: ca0e7c4e74390d805cf271dabc0e7aa7e58cd9635946c1ea654363bd87839347ba63290400ea6fd9ad95ea09a04c7a697b5448ca1aa073c3889d84281cc71bf4
[0] Computed HMAC Length: 128
[0] Received HMAC Length: 128
[0] Invalid HMAC signature.
I have tried testing using postman and ngrok and still can't get it to work. I have created several signature keys in authorize sandbox but still getting Invalid HMAC signature. Here is the result I get from payload
const verifyHMAC = (payload, receivedHMAC) => {
if (!receivedHMAC) {
console.error("Received HMAC is missing.");
return false;
}
const signatureWithoutPrefix = receivedHMAC.split("=")[1]?.trim();
if (!signatureWithoutPrefix) {
console.error("Invalid HMAC format received.");
return false;
}
console.log("Payload for HMAC:", JSON.stringify(payload));
const hmac = crypto.createHmac(
"sha512",
Buffer.from(process.env.AUTHORIZE_NET_SIGNATURE_KEY, "hex")
);
hmac.update(JSON.stringify(payload));
const computedHMAC = hmac.digest("hex");
console.log("Received HMAC:", signatureWithoutPrefix);
console.log("Computed HMAC:", computedHMAC);
console.log("Computed HMAC Length:", computedHMAC.length);
console.log("Received HMAC Length:", signatureWithoutPrefix.length);
if (computedHMAC.length !== signatureWithoutPrefix.length) {
console.error("Computed and received HMAC lengths do not match.");
return false; // No comparison happens if lengths are different
}
return crypto.timingSafeEqual(
Buffer.from(computedHMAC, "hex"),
Buffer.from(signatureWithoutPrefix, "hex")
);};
I am working with Nodejs and I want invoice to status to updated by net.authorize.payment.authcapture.created event. Payment is successful but the webhook signature I get Invalid HMAC signature. HMAC do not match
[0] Webhook Request Headers: {
[0] host: 'f273-97-64-131-130.ngrok-free.app',
[0] 'content-length': '438',
[0] 'content-type': 'application/json',
[0] expect: '100-continue',
[0] 'x-anet-signature': 'sha512=72A339F0FA63917346CAD6C5EFD84F20321BB1177459024C2A56B9602D1573D66BD27BEE09858843FEA80885C6BE3F0AA2A5A3C866D9261FE72774F87BCD7031',
[0] 'x-forwarded-for': '198.241.206.38',
[0] 'x-forwarded-host': 'f273-97-64-131-130.ngrok-free.app',
[0] 'x-forwarded-proto': 'https',
[0] 'x-opnet-transaction-trace': '9903cec4-6192-4042-9bce-b5b44bd782f2-11088-50965',
[0] 'accept-encoding': 'gzip'
[0] }
[0] Payload for HMAC: {"notificationId":"43396377-04b6-4c52-bb7f-5209d6376daa","eventType":"net.authorize.payment.authcapture.created","eventDate":"2025-02-06T04:38:23.3448159Z","webhookId":"d516f763-b101-459d-abed-3acbd836965a","payload":{"responseCode":1,"authCode":"MQ3APZ","avsResponse":"Y","authAmount":4000,"fraudList":[{"fraudFilter":"Amount Filter","fraudAction":"report"}],"invoiceNumber":"2025-0030","entityName":"transaction","id":"80037536431"}}
[0] Received HMAC: 72A339F0FA63917346CAD6C5EFD84F20321BB1177459024C2A56B9602D1573D66BD27BEE09858843FEA80885C6BE3F0AA2A5A3C866D9261FE72774F87BCD7031
[0] Computed HMAC: ca0e7c4e74390d805cf271dabc0e7aa7e58cd9635946c1ea654363bd87839347ba63290400ea6fd9ad95ea09a04c7a697b5448ca1aa073c3889d84281cc71bf4
[0] Computed HMAC Length: 128
[0] Received HMAC Length: 128
[0] Invalid HMAC signature.
I have tried testing using postman and ngrok and still can't get it to work. I have created several signature keys in authorize.net sandbox but still getting Invalid HMAC signature. Here is the result I get from payload
const verifyHMAC = (payload, receivedHMAC) => {
if (!receivedHMAC) {
console.error("Received HMAC is missing.");
return false;
}
const signatureWithoutPrefix = receivedHMAC.split("=")[1]?.trim();
if (!signatureWithoutPrefix) {
console.error("Invalid HMAC format received.");
return false;
}
console.log("Payload for HMAC:", JSON.stringify(payload));
const hmac = crypto.createHmac(
"sha512",
Buffer.from(process.env.AUTHORIZE_NET_SIGNATURE_KEY, "hex")
);
hmac.update(JSON.stringify(payload));
const computedHMAC = hmac.digest("hex");
console.log("Received HMAC:", signatureWithoutPrefix);
console.log("Computed HMAC:", computedHMAC);
console.log("Computed HMAC Length:", computedHMAC.length);
console.log("Received HMAC Length:", signatureWithoutPrefix.length);
if (computedHMAC.length !== signatureWithoutPrefix.length) {
console.error("Computed and received HMAC lengths do not match.");
return false; // No comparison happens if lengths are different
}
return crypto.timingSafeEqual(
Buffer.from(computedHMAC, "hex"),
Buffer.from(signatureWithoutPrefix, "hex")
);};
Share
Improve this question
asked Feb 6 at 5:12
John AcheroJohn Achero
211 bronze badge
2
- I think your payload was modified or something, for this kind of check you need to use raw body – Yuvaraj M Commented Feb 6 at 5:22
- Also raw body does not needs to stringify – Yuvaraj M Commented Feb 6 at 5:23
1 Answer
Reset to default 3You should use raw request body (payload) in order to verify the signature.
Also I see a vulnerability in your code, You need to compare the whole string not Signature length.
const computedHMAC = crypto.createHmac(
"sha512",
Buffer.from(process.env.AUTHORIZE_NET_SIGNATURE_KEY, "hex")
).update(rawBody).digest("hex"); // here you need to user rawBody
console.log("Received HMAC:", signatureWithoutPrefix);
console.log("Computed HMAC:", computedHMAC);
console.log("Computed HMAC Length:", computedHMAC.length);
console.log("Received HMAC Length:", signatureWithoutPrefix.length);
if (computedHMAC !== signatureWithoutPrefix) { // compare the whole string
console.error("Computed and received HMAC lengths do not match.");
return false; // No comparison happens if lengths are different
}