Hmac Verification for Bulk Actions

Solved
ikolarov
Shopify Partner
101 17 27

Hi.

 

It seems that I'm doing something wrong when trying to verify the hmac for the Bulk action.

 

My code is taken from the koa-shopify-auth:

 

 

const querystring = require('querystring');
const crypto = require('crypto');

const apiSecret = "APP_SECRET";
const {hmac: _hmac, signature: _signature, ...map} =  req.query;

let orderedMap = Object.keys(map)
  .sort()
  .reduce((accum, key) => {
    accum[key] = map[key];
    return accum;
  }, {});

const message = querystring.stringify(orderedMap);
const generatedHash = crypto
  .createHmac('sha256', apiSecret)
  .update(message)
  .digest('hex');

console.log(generatedHash, _hmac, message);

// generatedHash => 0f01a761392920bd30ef605590accce89a8280891cbcb671a2e1d4b3bd59ea60 
// _hmac         => 7c3f5fae53c3a5017146c4e23bd6af0fbe35054e4f009572860f39eb325c28a9 
// message => ids=4266725539892&ids=4268494946356&locale=en&object=product&shop=MY_STORE.myshopify.com&single=false&t
imestamp=1571296723

 The issue is that it doesn't work for multiply ids.

 

Example query response: 

 

 

 { 
  locale: 'en',
  object: 'product',
  shop: 'MY_STORE.myshopify.com',
  single: 'false',
  timestamp: '1571296530',
  ids: ['4266725539892', '4268494946356'],
  hmac: '0f01a761392920bd30ef605590accce89a8280891cbcb671a2e1d4b3bd59ea60'
}

The code work for a single id, so the issue should be in the ids array. 

 

Has someone faced this issue before?

 

What have I tried by guessing:

  1. join(',') for the ids array - fail
  2. changing the querystring variable from ids= to ids[]= - fail
  3. changing the word argument ids to id - fail
  4. using a single id - fail
  5. removing the additional query params ( object and single ) - fail
Accepted Solution (1)
Conner_Pope
Shopify Partner
48 1 19

This is an accepted solution.

hello @ikolarov , I have actually figured out what you need to do. It's a bit confusing but this is how I made it work.

 

When you get a bulk product request, you will receive a URL query like this: shop=random.myshopify.com&ids%5B%5D=1&ids%5B%5D=2&ids%5B%5D=3

 

What you must do is parse this URL query. What I did is I created an array, and for each ids%5B%5D within the query I add the value (object ID) to the array. I then send this to the backend to verify the Hmac. 

 

So, on the backend where you actually verify the Hmac, this is what Shopify expects... They expect you to add this array into the sorted query for the hmac calculated signature.

 

What this means is in your sorted query string, you must add ids to be in this (string) format: ids=["1", "2"] ... Note, this is not an array data type, this is a string that you must print out using the array sent from the client. Also note, the formatting needs to be precise, you must have double quotations ("), and you must add a single character of white space in between values.

 

This is what you must do. I have no idea why there is no documentation on any of this.

View solution in original post

Replies 9 (9)
Conner_Pope
Shopify Partner
48 1 19

Let me know what you come up with. I'm essentially having the same exact issue. I saw this thread here providing more details on what is expected from the hmac calculation but it's still failing for me. For reference here is the query object I'm using to validate HMAC:

 

{ hmac:
'insert_hmac_here',
locale: 'en',
timestamp: '1571680135',
shop: 'xxxxxxxx.myshopify.com',
ids: [ '1' ] }

ikolarov
Shopify Partner
101 17 27

Hi @Conner_Pope 

 

I haven't found a solution yet.

 

I saw the thread before posting but it didn't work for me as well.

 

A saw a few APPs that have the option for Bulk Actions and not working, so I it seemed that the issue was recent or it wasn't working from the start.

Conner_Pope
Shopify Partner
48 1 19

This is an accepted solution.

hello @ikolarov , I have actually figured out what you need to do. It's a bit confusing but this is how I made it work.

 

When you get a bulk product request, you will receive a URL query like this: shop=random.myshopify.com&ids%5B%5D=1&ids%5B%5D=2&ids%5B%5D=3

 

What you must do is parse this URL query. What I did is I created an array, and for each ids%5B%5D within the query I add the value (object ID) to the array. I then send this to the backend to verify the Hmac. 

 

So, on the backend where you actually verify the Hmac, this is what Shopify expects... They expect you to add this array into the sorted query for the hmac calculated signature.

 

What this means is in your sorted query string, you must add ids to be in this (string) format: ids=["1", "2"] ... Note, this is not an array data type, this is a string that you must print out using the array sent from the client. Also note, the formatting needs to be precise, you must have double quotations ("), and you must add a single character of white space in between values.

 

This is what you must do. I have no idea why there is no documentation on any of this.

ikolarov
Shopify Partner
101 17 27

Hi @Conner_Pope 

 

Thank you!

 

I gave up on this and I was considering even not adding the functionality to the App.

 

I can't stress enough how dumb is this. As you said this should be properly documented somewhere.

 

Tested it and it works without a problem. 

 

Thanks once again for the solution!

jordanskole
Shopify Partner
5 0 6

Wow @Conner_Pope thank you so much for figuring this out - i have no idea how you did it. I was banging my face against the wall trying to figure this out. This is crazy dumb and confusing and agree should be documented somewhere. 

 

Just to confirm, the sting to match against should look like (not included):

 

`ids=["1", "2"]&locale=en-US&shop=example.myshopify.com&timestamp=981723`

 

I'll verify and update for confirmation. 

 

My guess based on this is that nobody with a bulk action app extension is validating requests actually come from shopify 🥴

 

 

banned
PhobosTech
Shopify Partner
80 3 14

No kidding?

Why, for all sense and logic, would they design it like that?

Actually, it's probably because of the 2048 URL character limit. But whatever. There should be some documentation on this.

Thanks man. I never ended up adding this feature because I couldn't figure it out.

 

Most people, it turns out, just aren't interested unless they have to pay for it. Go figure.
PhobosTech
Shopify Partner
80 3 14

Alright man, I just need to make sure I'm following what you're saying ...

 

When you get a bulk product request, you will receive a URL query like this: shop=random.myshopify.com&ids%5B%5D=1&ids%5B%5D=2&ids%5B%5D=3

What you must do is parse this URL query. What I did is I created an array, and for each ids%5B%5D within the query I add the value (object ID) to the array.

So, on the backend where you actually verify the Hmac, this is what Shopify expects... They expect you to add this array into the sorted query for the hmac calculated signature.

What this means is in your sorted query string, you must add ids to be in this (string) format: ids=["1", "2"] ... Note, this is not an array data type, this is a string that you must print out using the array sent from the client. Also note, the formatting needs to be precise, you must have double quotations ("), and you must add a single character of white space in between values.

Alright, I'm going to pseudo code this, just to keep it language agnostic ... but this is what I'm getting from what you're saying...

// `shop=random.myshopify.com&ids%5B%5D=1&ids%5B%5D=2&ids%5B%5D=3`

getArray = SRV[GET]
getIds = SRV[GET]['ids']

hmac = getArray.pop('hmac')
getArray.pop('ids')

getArray.sort(_LEX_)

urlQuery = HTTP_UTIL.build_query(getArray)
urlQuery.substr_replace('/%5B%5D[*.]$/','')

urlQuery.append('=["'+getIds[0]+`",\s"`+getIds[1]+'"]')

// so my string should now look like this ... correct? note, the \s is really just for show
// i'm just curious if this is what you mean by whitespace between values

// `shop=random.myshopify.com&ids=["2295083958321",\s"2612413792305"]`

// then from there, I do the usual?

hashed = HMAC.hash( algo:'sha256', data:urlQuery, key:__API_SECRET_KEY__ )

if(HMAC.compare(hmac == hashed)){
return TRUE
}else{
return FALSE
}

 

Did I get that right? Is this what you're saying?

Most people, it turns out, just aren't interested unless they have to pay for it. Go figure.
policenauts
Shopify Partner
204 10 65

Thank you for this thread. However, this isn't working for me even when I turn:

 

hmac=d0891aa4b5b829eb515914624ff2242f859e671e23784d45cb6337e50c5e2788&host=Zmp0ZXN0aW5nMi5teXNob3BpZnkuY29tL2FkbWlu&ids%5B%5D=6579882721444&ids%5B%5D=6579882786980&locale=en-US&session=5bb5bac200df59c2963f405f7a0a25d52afcb36933057c669dbb45b6f942bd27&shop=fjtesting2.myshopify.com&timestamp=1645475039

 

Into

 

hmac=9c9819f08a66da9762e85496d63651d112497c3bc5731347fb7dc6c17e8141c3&host=Zmp0ZXN0aW5nMi5teXNob3BpZnkuY29tL2FkbWlu&ids=["6579882721444", "6579882786980"]&locale=en-US&session=5bb5bac200df59c2963f405f7a0a25d52afcb36933057c669dbb45b6f942bd27&shop=fjtesting2.myshopify.com&timestamp=1645473957

 

I've tried sticking the ids array at the end as well, no joy. Any idea what I'm doing wrong? Thank you.

shippr
Shopify Partner
1 0 0

Did you manage to find a solution? We're facing the same issue and can't seem to find anything helpful in the documentation.