UNPKG

@code-nl/shopify-hmac

Version:

Shopify HMAC lib to verify Shopify calls

72 lines (58 loc) 1.91 kB
"use strict"; const { hmacTimingSafeEqual, getHmacFromString } = require("./hmac"); /** * Get the request object translated into a string as of Shopify would have signed it. * * @param {Object} request The request object * @return {String} The query string as it would have been signed by Shopify */ exports.getSignableMessage = (request) => { // clone the query object const map = Object.assign({}, request.query); // leave out the hmac itself delete map.signature; // get the querystring as a string const message = Object.keys(map) .sort() .reduce((accumulator, keyName) => { // value can be both a plain value and an array const value = Array.isArray(map[keyName]) ? map[keyName].join(",") : map[keyName]; return [accumulator, `${keyName}=${value}`].join(""); }, ''); return message; }; /** * Generate a signature based on a query object. * * @param {Object} request The request object * @param {String} secret API Secret * @return {String} Generated HMAC */ exports.generate = (request, secret) => { // get a signable message const message = module.exports.getSignableMessage(request); // generate hmac based on the message const generatedHmac = getHmacFromString(message, secret, "hex"); return generatedHmac; }; /** * Verify request is from Shopify. * HMAC verification based on the query string that is transmitted. * * @param {Object} request Request Query object * @param {String} secret API Secret * @return {Bool} */ exports.verify = (request, secret) => { // get included hmac const { signature } = request.query; // guard: hmac is a required property if (typeof signature !== "string" || !signature) { return false; } // generate HMAC const generatedHmac = module.exports.generate(request, secret); // verify const valid = hmacTimingSafeEqual(signature, generatedHmac); return valid; };