Asked 1 month ago by CelestialCosmonaut848
How can I resolve the invalid HMAC signature for Authorize.Net webhooks in Node.js?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by CelestialCosmonaut848
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I'm working with Node.js to update invoice statuses based on the net.authorize.payment.authcapture.created event from Authorize.Net. Although the payment is successful, I'm encountering an "Invalid HMAC signature" error because the computed HMAC does not match the received signature.
I have tried using Postman and ngrok for testing, and I have generated several signature keys in the Authorize.Net sandbox, but the issue persists. Below is the webhook log output showing the header and payload details:
BASH[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.
Below is the snippet of my HMAC verification function:
JAVASCRIPTconst 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") ); };
Any help to troubleshoot why the signature verification is failing is appreciated.
Comments 0
•Answers 2
•Views 33
Answer by TychoAI Agent • 1 month ago
Below is a list of common issues and suggestions you can check to resolve the invalid HMAC signature:
When Authorize.Net generates the signature they hash the exact JSON string sent in the request. By parsing the JSON into an object and then re‑stringifying it, you may inadvertently change the whitespace or key order. Instead, compute the HMAC using the raw request body data. If you’re using Express, you can capture the raw body by using a custom body parser or middleware. For example:
JAVASCRIPT// Capture the raw body in Express app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } })); app.post('/webhook', (req, res) => { const rawPayload = req.rawBody; const receivedHMAC = req.headers['x-anet-signature']; if (verifyHMAC(rawPayload, receivedHMAC)) { // Process your webhook and change invoice status res.sendStatus(200); } else { res.sendStatus(400); } });
And update your verification function to use the raw payload (which is a Buffer):
JAVASCRIPTconst verifyHMAC = (rawPayload, receivedHMAC) => { if (!receivedHMAC) { console.error("Received HMAC is missing."); return false; } // Remove the prefix ("sha512=") const signatureWithoutPrefix = receivedHMAC.split("=")[1]?.trim(); if (!signatureWithoutPrefix) { console.error("Invalid HMAC format received."); return false; } // Use the raw payload directly without stringifying an object const hmac = crypto.createHmac( "sha512", Buffer.from(process.env.AUTHORIZE_NET_SIGNATURE_KEY, "hex") ); hmac.update(rawPayload); const computedHMAC = hmac.digest("hex"); console.log("Received HMAC:", signatureWithoutPrefix); console.log("Computed HMAC:", computedHMAC); if (computedHMAC.length !== signatureWithoutPrefix.length) { console.error("Computed and received HMAC lengths do not match."); return false; } return crypto.timingSafeEqual( Buffer.from(computedHMAC, "hex"), Buffer.from(signatureWithoutPrefix, "hex") ); };
Ensure that the value of process.env.AUTHORIZE_NET_SIGNATURE_KEY
is in the correct hex format. Check the following:
For example, if it’s not in hex, you might instantiate the key like so:
JAVASCRIPTBuffer.from(process.env.AUTHORIZE_NET_SIGNATURE_KEY, "utf8")
Only use "hex"
if you are sure the key is a hex string.
Double-check that the payload you use for creating the HMAC is exactly the same string that Authorize.Net signed. Any differences (even in a minor whitespace) will break the HMAC. This is why using the raw body captured in middleware is the preferred method.
When testing with Postman or any other tool, ensure that:
By ensuring you hash the exact raw string and that your signature key is formatted correctly, your computed HMAC should match the one provided in the request header. This should resolve the “Invalid HMAC signature” error.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by AstroNomad669 • 1 month ago
You 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.
JAVASCRIPTconst 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 }
No comments yet.
No comments yet.