Signature does not match on scratchpad - but method works for other endpoint


#1

Hi,

I’m trying to sign a request for the MWS Delete Subscription endpoint for the Subscription service in NodeJS. I’ve successfully managed to sign a request to create a subscription however when I’m trying to delete a subscription, my signature is apparently invalid. I’m using the exact same signing methodology as with the CreateSubscription request and at this point I’m completely confused how it can work with one operation but not the other.

I’ve tried using scratchpad and copying the timestamp and even checked line by line to ensure my key’s are in the exact same order, but I’m still not getting the signature scratchpad generated in comparison.

Here’s how I’m building the request:
const buildDeleteSubscriptionQuery = (mwsSellerId, mwsAuthToken, marketplace) => {
return {
AWSAccessKeyId: process.env.MWS_ACCESS_KEY_ID,
Action: ‘DeleteSubscription’,
MWSAuthToken: mwsAuthToken,
MarketplaceId: marketplace.marketplaceId,
SellerId: mwsSellerId,
SignatureMethod: ‘HmacSHA256’,
SignatureVersion: 2,
‘Destination.AttributeList.member.1.Key’: ‘sqsQueueUrl’,
‘Destination.AttributeList.member.1.Value’: process.env.MWS_SQS_QUEUE_URL,
‘Destination.DeliveryChannel’: ‘SQS’,
NotificationType: ‘AnyOfferChanged’,
Timestamp: new Date().toISOString(),
Version: ‘2013-07-01’
}
};

const generateDeleteSubscriptionRequest = (marketplaceUrl, deleteSubscriptionRequest) => {
return [‘POST’, marketplaceUrl, ‘/Subscriptions/2013-07-01’, qs.stringify(deleteSubscriptionRequest)].join(’\n’);
};

This will generate something like:
POST
mws-eu.amazonservices.com
/Subscriptions/2013-07-01
AWSAccessKeyId=some-key&Action=DeleteSubscription&SellerId=some-seller-id&MWSAuthToken=an-auth-token&SignatureVersion=2&Timestamp=2020-06-21T13%3A48%3A09Z&Version=2013-07-01&SignatureMethod=HmacSHA256&MarketplaceId=A1F83G8C2ARO7P&NotificationType=AnyOfferChanged&Destination.DeliveryChannel=SQS&Destination.AttributeList.member.1.Key=sqsQueueUrl&Destination.AttributeList.member.1.Value=https%3A%2F%2Fsqs.eu-west-1.amazonaws.com%2F1234567890%2Fsome-queue

To generate the signature, I’m using the following code:
const createSignatureForRequest = (request) => {
return crypto
.createHmac(‘sha256’, process.env.MWS_SECRET_KEY)
.update(request)
.digest(‘base64’);
};

As far as I can see, this should be correct.

Any ideas?


#2

The scratchpad shows the string to sign, the request, and the signature intermediaries.
You should be able to tell if your string to sign differs from the scratchpad, and if not where exactly in the process it fails to match. (string to sign, hmac digest, BASE64 representation, final request)

Some common things to double-check:

  1. failure to URLencode timestamp before signature digest, or using a fresh timestamp in the request (or doing anything to parms other than add signature between signing and request)
  2. failure to sort the parameters before signature digest. If the text you showed as the output was the string to sign, it does not have the parameters sorted correctly. Remember that node.js dictionaries (object list) are unordered
  3. failure to URLencode the resulting signature. This can fail intermittently since not all sigs need trailing ‘=’ padding or contain ‘+’ (also make sure you aren’t URLencoding blanks as ‘+’ signs)
  4. silly and hard to find typos, like adding in extra linefeeds or ‘/’ in the request or using inconsistent quotes on parameters. You do seem to specify the Destination parms differently than the rest.
  5. platform newline quirks, some platform/language combos don’t make \n and chr(10) equivalent

#3

Thank you for your reply.

When you say “If the text you showed as the output was the string to sign, it does not have the parameters sorted correctly” - how should the params be ordered exactly?

As for the signature, that gets added after the params have been signed in which case “Signature” appears as the last param. Is this correct?

Thanks,
Owen


#4

The parameters need to be alphabetically sorted before signing, the order is unimportant in the actual call as long as all the parms are there and unchanged.

The scratchpad shows the exact string that needs to be signed.


#5

I’ve just reordered my requestBuilder method to generate a request in the exact same order the string to sign query params in scratchpad displays and it’s still complaining that it does not match. If I take the string to sign from scratchpad and run it through my method that signs the signtature and URL encode it, it matches the one that scratchpad displays so I now know it’s nothing to do that with that.

I compared the query param to the query param my app generates and ran it through a diff checker and the only thing that is different was the minutes and seconds in the time stamp.

Now I’m really puzzled. The only difference is the Signature is added at the end of the query params whereas in scratchpad is part of the alphabetical ordering of the query params which from what you said, shouldn’t matter.

Here’s the diff I did.

The one my app generated:
AWSAccessKeyId=someAccessKey&Action=DeleteSubscription&Destination.AttributeList.member.1.Key=sqsQueueUrl&Destination.AttributeList.member.1.Value=https%3A%2F%2Fsqs.eu-west-1.amazonaws.com%2F1234567890%2Fsome-queue&Destination.DeliveryChannel=SQS&MWSAuthToken=someAuthToken&MarketplaceId=A1F83G8C2ARO7P&NotificationType=AnyOfferChanged&SellerId=someSellerId&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-06-21T19%3A52%3A30Z&Version=2013-07-01

The one scratchpad generated:
AWSAccessKeyId=someAccessKey&Action=DeleteSubscription&Destination.AttributeList.member.1.Key=sqsQueueUrl&Destination.AttributeList.member.1.Value=https%3A%2F%2Fsqs.eu-west-1.amazonaws.com%2F1234567890%2Fsome-queue&Destination.DeliveryChannel=SQS&MWSAuthToken=someAuthToken&MarketplaceId=A1F83G8C2ARO7P&NotificationType=AnyOfferChanged&SellerId=someSellerId&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-06-21T19%3A06%3A36Z&Version=2013-07-01

You can see that they’re identical apart from the timestamp.

If I compare the full string to sign from both scratchpad and my app, they also match apart from the timestamp.


#6

It sounds like you are getting the parms and signature correct now, you just need to figure out what is wrong with your POST request mechanics. It should contain the prefix ‘/Subscriptions/2013-07-01?’ and have the &Signature= parm added at the end.

What is different about your request than the one shown on the scratchpad? (not the string to sign parms that you are showing above, I think you have that part)


#7

@eoutlet

We are wrapping up the TypeScript / NodeJS MWS library right now.

You can see how we do subscriptions, and there is an integration test for it that is verified to work:

The library is not on NPM yet, but if you want to use it, we can publish an alpha release soon.


#8

Got it to work finally! Feels good.

I think it was just the ordering of the request when built up as an object and removing encodedURIComponent for the signature (URL encoding the signature is what was causing the mismatch after the ordering was corrected).

I appreciate the help, this is one of the most strictest API’s I’ve ever tried integrating with. Using the .NET C# I was using previously was far easier but it certainly needs a javascript library too or even porting their existing .NET library to .NET core now that we have the likes of AWS etc.

@Roman_ScaleLeap.com certainly interested in this. Pop me a message when you have an alpha on npm.

Thank you,
Owen