App Submission - Automated checks - Verifies webhooks with HMAC signatures

Hey,

I am struggling with the automated check for HMAC validation.

I can find some posts online stating that using a custom implementation of the validation works for them but it does not work for me.

I have and external admin dashboard as a NextJS app hosted on Vercel.

My webhooks are properly configured and work well on dev and prod.

I have both a custom hmac validator and the @shopify/shopify-api shopifyApi.webhooks.validate up and I can see that both are working fine using logs in prod and dev.

However - the automated check is consistently failing and I have no way to debug or get any information as to what exactly isn’t working or up to standard.

This is very frustrating and discouraging after spending a long time developing my app.

Please advice as to how to move forward as I am currently stuck.

Thanks,

Kfir Golan

1 Like

Hey Kfir,

The logs you mention, are those the ones in the developer dashboard, or somewhere else? If you haven’t checked the developer dashboard yet, that may give more insight in to how the webhooks are being received on our end.

You can also see some common validation pitfalls here: Deliver webhooks through HTTPS

  • Raw Body Parsing: Shopify’s HMAC verification requires the raw request body. If you’re using a body parser middleware like express.json(), it will parse the body before your webhook verification code gets to it. You need to capture the raw body before it’s parsed.
  • Buffered Raw Body: You should use the raw buffered body for the HMAC calculation.
  • Middleware Order: Ensure that your webhook verification middleware is placed before any body parsing middleware in your app.
  • Encoding: Ensure your encoding is set properly.

Hey @KyleG-Shopify Thanks for the reply :folded_hands:

I can’t see anything in the developer dashboard that can explain why the HMAC check is failing.

I went as far as to consume the request body buffer manually - the code looks like this:

export async function getRawBody(request: Request) {
  const clonedRequest = request.clone()
  // Create a reader for the raw body stream
  const reader = clonedRequest.body?.getReader()
  const chunks: Uint8Array[] = []

  if (reader) {
    let done = false
    while (!done) {
      const { value, done: readerDone } = await reader.read()
      if (value) {
        chunks.push(value)
      }
      done = readerDone
    }
  }

  // Combine all chunks into a single Buffer
  const rawBody = Buffer.concat(chunks)

  return rawBody
}

After consuming the body I check the HMAC using the crypto node lib:

// rawBody is the output of the getRawBody function above
export async function validateHmac(rawBody: Buffer, request: Request) {
  const shopifyHmac = request.headers.get(ShopifyHeader.Hmac)

  const calculatedHmacDigest = crypto.createHmac('sha256', env.SHOPIFY_CLIENT_SECRET).update(rawBody).digest('base64')

  const hmacValid = crypto.timingSafeEqual(
    Buffer.from(calculatedHmacDigest, 'base64'),
    Buffer.from(shopifyHmac!, 'base64'),
  )

  return hmacValid
}

I can see this working in dev when triggering a webhook from the CLI.

Any help here would be greatly appreciated as this is all that keeps me from submitting my app.

Thanks

Thanks @kfiroo. I would say at this point it would be worthwhile to reach out to our support team on this so they can take a look at your current app setup and see if anything stands out that would be blocking this check.

Sure thanks @KyleG-Shopify

Hey @kfiroo, checking in to make sure you were able to get this resolved.