FROM CACHE - en_header

Return management just got easier! We’ve launched Customer Self-Serve Returns to all Shopify merchants. Click here to learn more!

Can't verify Webhooks in Node.js! (serverless AWS lambda)

Zolbayar
Shopify Partner
46 1 8

That exclamation point used in the title is not an accident. 

 

1. The official doc doesn't have an example for Node.js 

 

2. On this Shopify Partners' blog post, it says: 

 

exports.handler = (event, context, callback) => {
  var client_secret = event.client_secret;
  delete event.client_secret;

  //calculate the hash
  var calculated_hash = crypto.createHmac("sha256", client_secret).update(new Buffer(event.body, "base64")).digest("base64");

  //reject the message if the hash doesn't match
  if (event["X-Shopify-Hmac-SHA256"] != calculated_hash) {
    console.log("calculated_hash: (" + calculated_hash + ") != X-Shopify-Hmac-SHA256: (" + event["X-Shopify-Hmac-SHA256"] + ")");
    return;
  }
}

This post was specifically for an AWS lambda function. But when I generate the hash like this:

 

 

var calculated_hash = crypto.createHmac("sha256", client_secret).update(new Buffer(event.body, "base64")).digest("base64");

It's still different from the `X-Shopify-Hmac-Sha256` header value. 

 

3. I tried it with the following different ways: 

  • update(Buffer.from(event.body, "base64"))
  • update(Buffer.from(JSON.stringify(event.body), 'utf8', 'hex'))
  • update(event.body)

4. I replaced `client_secret` with the following values: 

  • "API secret key" from partners.shopify.com → Apps → App
  • "API key" from partners.shopify.com → Apps → App
  • "Access token" I got from the store when we installed the app

All the docs say it's either "client secret" or "shared secret". But I can't find any value with that exact name. Am I using the wrong secret? Or am I using a wrong method to generate the hash? 

 

Working remotely from Mongolia ~ www.gereesee.com
Replies 23 (23)
theschoolofux
Shopify Partner
10 0 3

Had the same issue. Using request.rawBody instead of request.body helped:

 

import Router from "koa-router";
import koaBodyParser from "koa-bodyparser";
import crypto from "crypto";

...

koaServer.use(koaBodyParser()); 

...

koaRouter.post(
    "/webhooks/<yourwebhook>",
    verifyShopifyWebhooks,
    async (ctx) => {
      try {
        ctx.res.statusCode = 200;
      } catch (error) {
        console.log(`Failed to process webhook: ${error}`);
      }
    }
);

...

async function verifyShopifyWebhooks(ctx, next) {
  const generateHash = crypto
    .createHmac("sha256", process.env.SHOPIFY_WEBHOOKS_KEY) // that's not your Shopify API secret key, but the key under Webhooks section in your admin panel (<yourstore>.myshopify.com/admin/settings/notifications) where it says "All your webhooks will be signed with [SHOPIFY_WEBHOOKS_KEY] so you can verify their integrity
    .update(ctx.request.rawBody, "utf-8")
    .digest("base64");

  if (generateHash !== shopifyHmac) {
    ctx.throw(401, "Couldn't verify Shopify webhook HMAC");
  } else {
    console.log("Successfully verified Shopify webhook HMAC");
  }
  await next();
}

 

Sergei Golubev | Devigner @ The School of UX | schoolofux.com
Luis45
New Member
6 0 0

Hi, I have the same problem. but i am not using "crypto", I'm using "crypto-js" Search the module "'crypto" but I did not find it and the module "crypto-js"doesn't have the func "createHmac"..

 

 

could you tell me how to find the module "crypto" and upload a lambda aws..? 

Luis45
New Member
6 0 0

Hi,

 

Did you ever happen to get this working? , I have the same problem

Zolbayar
Shopify Partner
46 1 8

Hi @Luis45,

 

I made it work with this method: 

 

 

const verifyHMAC = (shopifyHmac, shopifyPayload) => {
  const hash = crypto
    .createHmac('sha256', process.env.SHOPIFY_PUBLIC_APP_SECRET)
    .update(shopifyPayload, 'utf8')
    .digest('base64');

  const isEqual = crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(shopifyHmac),
  );

  return isEqual;
};

The key was using providing only `utf8` to the `.update(shopifyPayload, 'utf8')`. Not '`base64' `or `'utf8', 'hex'`

 

Working remotely from Mongolia ~ www.gereesee.com