← back to Dw Signup Fulfillment

lib/webhook.js

30 lines

'use strict';
// Shopify webhook HMAC verification. Shopify signs the RAW request body with the
// app's webhook secret (SHA-256, base64) and sends it in X-Shopify-Hmac-Sha256.
// We MUST verify against the raw bytes — parsed/re-stringified JSON will not match.
const crypto = require('crypto');
const config = require('./config');

// verify(rawBody: Buffer|string, hmacHeader: string) -> boolean
function verify(rawBody, hmacHeader) {
  if (!config.SHOPIFY_WEBHOOK_SECRET) return false;
  if (!hmacHeader) return false;
  const digest = crypto
    .createHmac('sha256', config.SHOPIFY_WEBHOOK_SECRET)
    .update(Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody, 'utf8'))
    .digest('base64');
  const a = Buffer.from(digest);
  const b = Buffer.from(hmacHeader);
  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

// Helper for tests / go-live smoke: compute the header a given secret would produce.
function sign(rawBody, secret) {
  return crypto.createHmac('sha256', secret)
    .update(Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody, 'utf8'))
    .digest('base64');
}

module.exports = { verify, sign };