Shopify APP node js hmac validation

Topic summary

A developer is struggling to validate Shopify webhook HMAC signatures in a Node.js application. Despite following recommended practices, the generated hash consistently differs from the X-Shopify-Hmac-SHA256 header value.

Current implementation:

  • Uses bodyParser.raw() to access raw request body
  • Generates SHA256 HMAC using the API secret key
  • Compares base64-encoded hash with webhook header

Troubleshooting attempts:

  • Verified secret key matches across configurations
  • Tested multiple crypto libraries with identical results
  • Confirmed raw body data is being processed

Community suggestions:

  • Ensure raw body processing (using bodyParser.raw() or capturing via verify callback)
  • Verify the correct shared secret is being used

Current status: The issue remains unresolved. The developer suspects a potential discrepancy in secret keys between app and webhook configurations, though believes they should be identical. The validation logic appears correct, but hashes continue to mismatch.

Summarized with AI on November 1. AI used: claude-sonnet-4-5-20250929.

Hello, everyone, I have the same problem that was here.

In short, “hmac” from the webhook header and from my function are different, how can I fix it?

According to all the comments I’ve made some changes to fix it, I suppose that my hash function works correctly, I also work with raw data and obviously, I double-checked that my secret key is the same.

Also, I used different “crypto” services, but the result was always the same.

Here is my current simple code that I wrote just to test the “hmac” validation

import express from 'express';

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

const app = express();
const PORT = 9000;

app.use(bodyParser.urlencoded({ extended: true, limit: '100mb' }));

app.post('/shopify/webhooks/customer-data-request', bodyParser.raw({ type: 'application/json' }),async (req, res) => {
  try {
    const secret = 'SOME_API_SECRET_KEY'
    const hmac = req.headers['x-shopify-hmac-sha256'];

    const genHash = crypto
      .createHmac('sha256', secret)
      .update(req.body, 'utf8')
      .digest('base64');

    if (genHash !== hmac) {
      return res.status(401).send('Couldn\'t verify incoming Webhook request!');
    }

    //...do something
  } catch (error) {
    console.error(error);
  }
});

app.use(bodyParser.json());

app.listen(PORT, (err) => {
  if (!err) {
    console.log(`App Listen ${PORT}`);
  }
});

Hi,
Shopify webhook requests include a base64-encoded “X-Shopify-Hmac-SHA256” header that needs to be validated using your app’s shared secret and the request data.
You will want to ensure that you’re processing the raw body of the request when generating the hash

Hi @HRS_SRG

The problem is you use not raw body accordingly your code I thin you can make follow trick

app.use(bodyParser.json({
  verify: (req, res, buf) => {
    req.rawBody = buf
  }
}))

and req.rawBody

Hello, thanks for your help. Yes, I’m sure that all required data exists, and I’m sure that my body has a raw format.

I also have thoughts about secret keys, maybe shopify has different keys for the app and webhook for example, but I think the key is the same, so I don’t have an idea how to fix it, because as I told, it seems to logic works correctly but “hmac”
and “genHash” is always different

Hello, thanks for your answer. Yes, I also tried this method, but both of them gave me possibilities to work with raw data, so now I’m working with “bodyParser” just because it was the first solution that I found.

I think the main problem is not in the row data, but I can’t clearly understand what to do because it looks like everything ok in this code