Authenticating fetch_stock.json webhook request in remix app

It appears that authenticate is not able to handle Shopify requests to the fetch_stock.json webhook.

Webhook requests contain a x-shopify-hmac-sha256 header, but none of the the authenticate methods seem to be able to authenticate the requests.

I’ve tried the following methods without any success:
const { admin } = await authenticate.webhook(request);
const { admin } = await authenticate.admin(request);
const { admin } = await authenticate.fulfillmentService(request);

All of which fail.

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const { shop, topic, payload, admin } = await authenticate.webhook(request);
  // Never gets any further
  ...
};

As fetch_stock.json webhook requests are using GET, how do I use x-shopify-hmac-sha256 header to authenticate the request? Typically this header is used with the body of a request, which isn’t present in this case.

At present it seems that I must blindly trust the requests are from Shopify as there’s no clear way to authenticate them.

1 Like

Hey @gavinharriss, this is a great question.

We did some digging in to this and found this post in the Shopify Community with examples of how other developers are handling this:

Let me know if that helps.

Hey @gavinharriss, does the above help at all?

Hi @KyleG-Shopify, thank you very much for that, it’s much appreciated. It looks very likely to be the solution, I was just waiting off till I had an opportunity to try it before reporting back. I’m hoping to get some free time today to try it out.

@KyleG-Shopify thank you, I can confirm that the solution linked to almost worked.

The only change I found was needed is that the query string should be kept as-is, no unescape of values should be performed, and no sorting is required. If param values are decoded, then the computed hash will no longer match the HMAC header passed in.

My simplified working version:

function hmacPassesVerification(request: Request){
  const hmac = request.headers.get('x-shopify-hmac-sha256');

  const url = new URL(request.url);
  const hash = crypto.createHmac('sha256', process.env.SHOPIFY_API_SECRET!)
    .update(url.searchParams.toString(), 'utf8')
    .digest('base64');

  logger.debug('HMAC verification', { header: hmac, computed: hash, result: hmac === hash });

  return hash === hmac;
}
1 Like