shopify-api-js
shopify-api-js copied to clipboard
validateHmac returns false even if it is correct
Issue summary
API method Shopify.Utils.validateHmac
returns false even if query is correct
Reduced test case
import crypto from 'crypto';
import Shopify, { ApiVersion } from '@shopify/shopify-api';
const SHOPIFY_API_KEY = 'test';
const SHOPIFY_API_SECRET_KEY = 'secret';
const safeCompare = (hmac, queryStr) => (
hmac === crypto.createHmac('SHA256', SHOPIFY_API_SECRET_KEY).update(queryStr).digest('hex')
);
const stringifyQuery = (obj) => Object.entries(obj)
.filter((entry) => entry[0] !== 'hmac')
.map((entry) => `${entry[0]}=${entry[1]}`)
.join('&');
Shopify.Context.initialize({
API_KEY: SHOPIFY_API_KEY,
API_SECRET_KEY: SHOPIFY_API_SECRET_KEY,
SCOPES: ['read_products'],
HOST_NAME: 'example.com',
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
const url = new URL('https://example.com/?hmac=c0ffe062d215a3fa772aa0985dbae97db17a4c1996dc3f94ee1ae2bb097614e8&shop=sqd-dev.myshopify.com×tamp=1639749203');
const query = Object.fromEntries(url.searchParams);
const { hmac, ...rest } = query;
const valid1 = Shopify.Utils.validateHmac(query);
const valid2 = safeCompare(hmac, stringifyQuery(rest));
console.log({ query, valid1, valid2 });
Expected behavior
valid1
should be true
Actual behavior
It is false
I am also getting hmac validation as false for valid requests.
any solution I am facing same error
I switched to a custom validator.
import crypto from "crypto";
export function validateHmac(query) {
const safeCompare = (hmac: string, queryStr: string) => {
return (
hmac ===
crypto
.createHmac("SHA256", process.env.SHOPIFY_API_SECRET)
.update(queryStr)
.digest("hex")
);
};
const params = new URLSearchParams(query);
const hmac = params.get("hmac");
params.delete("hmac");
params.sort();
return safeCompare(hmac, params.toString());
}
you can use it as follows
const isHmacValid = validateHmac(ctx.query);
Same issue here
I'm also having the same issue. Though the same code works on a one Shopify App but it fails on another Shopify App. Here is one of the example which is failing-
[Nest] 29512 - 05/11/2022, 1:49:42 AM LOG [GET] /shopify/from-app-store?hmac=50d54a1742b32871db3284e39b39e9490e18b9edec91183403572320c9298986&host=Y29vZWUtZGVtby5teXNob3BpZnkuY29tL2FkbWlu&shop=cooee-demo.myshopify.com×tamp=1652212222
checkAuthenticity:154 {
hmac: '50d54a1742b32871db3284e39b39e9490e18b9edec91183403572320c9298986',
host: 'Y29vZWUtZGVtby5teXNob3BpZnkuY29tL2FkbWlu',
shop: 'cooee-demo.myshopify.com',
timestamp: '1652212222'
}
checkAuthenticity:156 false 69ef84a9d228216d68cb942d63f37e712819e38712616914cdaea22b7c6b2283
checkAuthenticity:169 true 50d54a1742b32871db3284e39b39e9490e18b9edec91183403572320c9298986
[Nest] 29512 - 05/11/2022, 1:49:42 AM DEBUG OperationFailedException- shopify.install.error.invalid-request
For a method like this-
checkAuthenticity(params: Record<string, any>): void {
console.log('checkAuthenticity:154', params);
const validHMAC = Shopify.Utils.validateHmac(params as AuthQuery);
console.log('checkAuthenticity:156', validHMAC, generateLocalHmac(params as AuthQuery));
const hmac = params.hmac;
delete params.hmac;
const queryString = Object.keys(params)
.map(key => `${key}=${params[key]}`)
.join('&');
const createdHMAC = crypto.createHmac('sha256', this.configService.get('SHOPIFY_APP_API_SECRET'))
.update(queryString)
.digest('hex')
.toString();
console.log('checkAuthenticity:169', createdHMAC === hmac, createdHMAC);
if (!validHMAC) {
throw new OperationFailedException('shopify.install.error.invalid-request');
}
}
Same issue, getting it consistently only on one shop with latest 5.x.x version, others work fine. Probably a bug in Shopify library to validate HMAC on specific content. Sample fail
InvalidWebhookError: Could not validate request for topic products/update
X-Shopify-Webhook-Id
4d5c23b0-7800-49b1-83b1-c962a5b24eeb
This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 14 days.
not stale
not stale
This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 14 days.
We are closing this issue because it has been inactive for a few months. This probably means that it is not reproducible or it has been fixed in a newer version. If it’s an enhancement and hasn’t been taken on since it was submitted, then it seems other issues have taken priority.
If you still encounter this issue with the latest stable version, please reopen using the issue template. You can also contribute directly by submitting a pull request– see the CONTRIBUTING.md file for guidelines
Thank you!
Still not working. Someone also opened another similar issue https://github.com/Shopify/shopify-api-js/issues/878
https://github.com/Shopify/shopify-api-js/blob/2bd69f862c166c5892cc8a8ceb9eecff6aa9aeb1/lib/utils/processed-query.ts#L11
https://github.com/Shopify/shopify-api-js/blob/2bd69f862c166c5892cc8a8ceb9eecff6aa9aeb1/lib/utils/processed-query.ts#L50
As described in the mentioned issue, I suppose this does not work (for app proxy signature?) because the query is parsed to URLSearchParams, which when converted to string it will be joined with &
. This differs from the reference implemention which joins the query strings without anything according to https://shopify.dev/docs/apps/online-store/app-proxies#calculate-a-digital-signature
not stale, still not working.