Verify webhook on nodejs problem

Verify webhook on nodejs problem

rdiazroman
Visitor
1 0 0

I'm unable to verify a Webhook in Node js.

 

I followed the solution proposed here but I cannot make it work:

https://community.shopify.com/c/webhooks-and-events/verify-webhook-on-nodejs/m-p/2172977/highlight/t...

 

I make sure that I use always the same SHOPIFY_WEBHOOK_SECRET.

But Received X-Shopify-Hmac-Sha256, and Calculated HMAC Hash never match. 

 

Here is the script to create the webhook:

 

import fetch from 'node-fetch';
import dotenv from 'dotenv';

// Load environment variables from .env file
dotenv.config();

const { SHOPIFY_STORE, SHOPIFY_API_KEY, SHOPIFY_API_PASSWORD, SHOPIFY_WEBHOOK_SECRET } = process.env;

// Function to create a Shopify webhook
const createShopifyWebhook = async () => {
  const url = `https://${SHOPIFY_STORE}/admin/api/2024-07/webhooks.json`;

  // Create the base64-encoded credentials for Basic Auth
  const credentials = Buffer.from(`${SHOPIFY_API_KEY}:${SHOPIFY_API_PASSWORD}`).toString('base64');  

  const webhookData = {
    webhook: {
      topic: 'orders/paid',  // The event that triggers the webhook
      address: 'https://my-heroku-www/webhook/order-paid',  // Your actual webhook URL
      format: 'json',  // Webhook payload format
      secret: SHOPIFY_WEBHOOK_SECRET  // Secret used to sign the webhook for HMAC verification
    }
  };

  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Basic ${credentials}`,  // Basic Authentication header
      },
      body: JSON.stringify(webhookData),  // Send webhook data
    });

    if (!response.ok) {
      throw new Error(`Error creating webhook: ${response.statusText}`);
    }

    const data = await response.json();
    console.log('Webhook created successfully:', data);
  } catch (error) {
    console.error('Error creating webhook:', error);
  }
};

createShopifyWebhook();

 

and here the webhook validation:

 

import express from 'express';
import bodyParser from 'body-parser';
import crypto from 'node:crypto';
import dotenv from 'dotenv';

dotenv.config();

const { SHOPIFY_WEBHOOK_SECRET } = process.env;

const app = express();
const port = process.env.PORT || 3000;

// Middleware to capture raw body for HMAC verification
app.use(bodyParser.raw({ type: 'application/json' }));

// HMAC Verification Middleware
const verifyShopifyWebhook = (req, res, next) => {
  const hmacHeader = req.headers['x-shopify-hmac-sha256'];
  
  // Generate the HMAC using the raw body
  const calculatedHmac = crypto
    .createHmac('sha256', SHOPIFY_WEBHOOK_SECRET)
    .update(req.body, 'utf8', 'hex')
    .digest('base64');
  
  console.log('Received X-Shopify-Hmac-Sha256:', hmacHeader);
  console.log('Calculated HMAC Hash:', calculatedHmac);

  const hmacBuffer = Buffer.from(hmacHeader, 'base64');
  const calculatedHmacBuffer = Buffer.from(calculatedHmac, 'base64');

  if (hmacBuffer.length !== calculatedHmacBuffer.length) {
    console.log('HMAC different lenghts');
    return res.status(401).send("Couldn't verify incoming Webhook request!");
  }

  if (!crypto.timingSafeEqual(hmacBuffer, calculatedHmacBuffer)) {
    console.log('HMAC comparison failed');  // <----------------- This is the output I get
    return res.status(401).send("Couldn't verify incoming Webhook request!");
  }
  
  console.log('Webhook verification successful');
  
  // Parse the raw body into JSON after verification
  req.body = JSON.parse(req.body.toString());
  next();  // Proceed to the next middleware or route handler
};

// Use the verification middleware before any route that needs verification
app.post('/webhook/order-paid', verifyShopifyWebhook, (req, res) => {
  // Handle the verified webhook here
  const payload = req.body;
  console.log('Webhook Payload:', payload);
  
  res.status(200).send('Webhook processed successfully');
});

// Start the server
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

I always get "HMAC comparison failed" 

 

I would need some help please, really don't know what is wrong...

 

Thank you very much

Reply 1 (1)

Kyle_liu
Shopify Partner
178 26 29

hi @rdiazroman 

 

You can try it.

const bodyParser = require('body-parser')
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())

 

const hmac = req.header("X-Shopify-Hmac-Sha256");
LOGGER.info(`hmac: ${hmac}`);

const genHash = crypto
.createHmac("sha256", process.env.APISECRET)
.update(JSON.stringify(req.body), "utf8", "hex")
.digest("base64");

console.log(genHash);

if (genHash !== hmac) {
return res.status(401).send("Couldn't verify incoming Webhook request!");
}
If this is helpful, please Like and Accept the solution.
Want to modify or custom changes on store? Let me help.
- Feel free to contact me on ([email protected])