Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Could not validate request HMAC #1296

Open
yanglee2421 opened this issue Sep 1, 2023 · 3 comments
Open

Could not validate request HMAC #1296

yanglee2421 opened this issue Sep 1, 2023 · 3 comments

Comments

@yanglee2421
Copy link

Issue summary

I am building a merchant-facing app to onboard merchants to my marketplace. After setting up shopifyApp from @shopify/shopify-app-express, the app it keeps giving the following error:

Webhook request is invalid, returning 401: Could not validate request HMAC
Failed to process webhook: Error: Could not validate request HMAC

Complete setup (removed key and secret to paste here):

// Shopify.ts
export const shopify = shopifyApp({
  api: {
    apiVersion: LATEST_API_VERSION,
    restResources,
    ...env(),
  },
  auth: {
    path: "/api/auth",
    callbackPath: "/api/auth/callback",
  },
  webhooks: {
    path: "/api/webhooks",
  },
  sessionStorage: new SQLiteSessionStorage(DB_PATH),
});

// GDPR.ts
import { DeliveryMethod } from "@shopify/shopify-api";
import { WebhookHandlersParam } from "@shopify/shopify-app-express";

export const webhookHandlers: WebhookHandlersParam = {
  CUSTOMERS_DATA_REQUEST: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: "/api/webhooks",
    async callback(topic: any, shop: any, body: any, webhookId: any) {
      const payload = JSON.parse(body);
      console.log("CUSTOMERS_DATA_REQUEST", webhookId);
    },
  },

  CUSTOMERS_REDACT: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: "/api/webhooks",
    async callback(topic: any, shop: any, body: any, webhookId: any) {
      const payload = JSON.parse(body);
      console.log("CUSTOMERS_REDACT", webhookId);
    },
  },

  SHOP_REDACT: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: "/api/webhooks",
    async callback(topic: any, shop: any, body: any, webhookId: any) {
      const payload = JSON.parse(body);
      console.log("SHOP_REDACT", webhookId);
    },
  },

  APP_UNINSTALLED: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: "/api/webhooks",
    async callback(topic: any, shop: any, body: any, webhookId: any) {
      const payload = JSON.parse(body);
      console.log("APP_UNINSTALLED", webhookId);
    },
  },

  PRODUCTS_CREATE: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: "/api/webhooks",
    async callback(topic: any, shop: any, body: any, webhookId: any) {
      const payload = JSON.parse(body);
      console.log("PRODUCTS_CREATE", webhookId);
    },
  },

  PRODUCTS_DELETE: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: "/api/webhooks",
    async callback(topic: any, shop: any, body: any, webhookId: any) {
      const payload = JSON.parse(body);
      console.log("PRODUCTS_DELETE", webhookId);
    },
  },
};
  • @shopify/shopify-app-express version:^2.2.2
  • Node version:v18.16.0
  • Operating system:Windows

Expected behavior

  • shopifyApp should setup correctly and link it provides should work

Actual behavior

Giving error:

Webhook request is invalid, returning 401: Could not validate request HMAC
Failed to process webhook: Error: Could not validate request HMAC

Steps to reproduce the problem

  1. git clone https://github.com/yanglee2421/shopify-app-demo.git
  2. yarn && yarn dev
  3. yarn shopify webhook trigger
@ernesto-ck
Copy link

Hi, any update on this, I have the same issue and I tried different solutions but nothing works

@yanglee2421
Copy link
Author

yanglee2421 commented Oct 10, 2023 via email

@rameardo
Copy link

rameardo commented Jul 21, 2024

To ensure that a webhook was sent from Shopify, you need to verify its authenticity by calculating a digital signature. Shopify includes a header x-shopify-hmac-sha256 with the request, which contains a base64-encoded HMAC-SHA256 hash of the request body. This hash is generated using the secret key you set in your webhook settings.

You can find more details here.

Here's an improved example in JavaScript:

function verifyWebhook(data, hmacHeader, CLIENT_SECRET) {
  const encoder = new TextEncoder();
  const keyData = encoder.encode(CLIENT_SECRET);

  return crypto.subtle.importKey('raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])
    .then(key => crypto.subtle.sign('HMAC', key, data))
    .then(signature => {
      const calculatedHmac = btoa(String.fromCharCode(...new Uint8Array(signature)));
      return calculatedHmac === hmacHeader;
    });
}



const clonedRequest = request.clone()
const hmac = clonedRequest.headers.get('x-shopify-hmac-sha256')

if (!hmac)
  return new Response('not shopify request', { status: 401 })

const CLIENT_SECRET = "YOUR_SHOPIFY_SECRET"
const body = await clonedRequest.arrayBuffer()
const isValid = await verifyWebhook(body, hmac, CLIENT_SECRET)

if (!isValid)
  return new Response('Invalid Signature/HMAC', { status: 401 })

// Continue with your logic

// return shopify 200 response
return new Response('OK', { status: 200 })

This code performs the following steps:

  1. Imports the client secret key for HMAC signing.
  2. Signs the request body with the imported key.
  3. Converts the signature to a base64 string.
  4. Compares the calculated HMAC with the HMAC received in the request header.
  5. Returns a 401 response if the HMAC verification fails or a 200 response if it succeeds.

I hope this helps! Let me know if you have further questions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants