hakanensari/vacuum

CartCreate method always returns SignatureDoesNotMatch

skatkov opened this issue · 11 comments

Problem

While using vacuum gem I can retrieve item_lookups or conduct search, but create_cart always returns SignatureDoesNotMatch.

"\n<CartCreateErrorResponse xmlns="http://ecs.amazonaws.com/doc/2013-08-01/\">SignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.b537c4f3-bfed-4c6a-95bf-*********"

Using same keys on API scratchpad, CreateCart works without any issues.

Why?

here is an signed url I end with on scratchpad:

https://webservices.amazon.com/onca/xml?AWSAccessKeyId=<KEY_ID>&AssociateTag=<TAG>&Item.1.ASIN=B07GLXZMR2&Item.1.Quantity=1&Operation=CartCreate&ResponseGroup=Cart&Service=AWSECommerceService&Timestamp=2018-11-13T17%3A06%3A41.000Z&Signature=b%2F0GFyhrMeWQhnHME4lXAaFNk0bkRKvQNx0CQeqVe3s%3D

and here is signed url through vacuum:

http://webservices.amazon.com/onca/xml?AWSAccessKeyId=<KEY_ID>&AssociateTag=<TAG>&Item.0.ASIN=B01DU5HOXW&Item.0.Quantity=1&Operation=CartCreate&Service=AWSECommerceService&Signature=0gZph1HJAiaVygu5GyDjOI7TJriBs2934D6V21MUF2g=&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2018-11-13T16:29:11Z&Version=2013-08-01

I see couple of differences:

  • 'Signature' is not at the end
  • Scratchpad doesn't use 'SignatureMethod' and 'SignatureVersion' and 'Version'

Based on this CartCreate method documentation and common request parameters
https://docs.aws.amazon.com/AWSECommerceService/latest/DG/CartCreate.html
https://docs.aws.amazon.com/AWSECommerceService/latest/DG/CommonRequestParameters.html

I can assume that 'SignatureMethod' and 'SignatureVersion' are not supported.

It's probably related to Jeff gem?


OLD DESCRIPTION

Good day.

I've noticed, that this gem implements cart_create method. But implementation seems incomplete.

I already wrote separate Query builder (to have a proper hash with items)
And now finishing signing for cart request (HMAC)

This gem doesn't handle this and I'm not sure why? Maybe it's separated into helper gem? Or maybe you just didn't had a demand for this... should I contribute back code I'll end up with?

@skatkov yes, it would definitely be nice to abstract away the awkward key names with a proper hash. I'm assuming this would in the same breath simplify this and this query to something similar to the following?

req.item_search(query: {
  item_search: {
    search_index: 'All',
    keywords: 'Foucault',
    response_group: 'ItemAttributes,Images,Offers',
    item_page: [1, 2]
  }
}

About HMAC, maybe I'm not reading the API docs right, but what I understand is this is passed to you when you create a cart and then you use it in subsequent requests. I'm not sure if we need to build any functionality around this here?

@hakanensari

Good example how CardCreate works could be looked up from API scratchpad.

On 'CardCreate' page scroll all way down to code snippers section, pick ROR in the tab.

Here are last lines from that snippet.

# Generate the canonical query
canonical_query_string = params.sort.collect do |key, value|
  [URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")), URI.escape(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))].join('=')
end.join('&')

# Generate the string to be signed
string_to_sign = "GET\n#{ENDPOINT}\n#{REQUEST_URI}\n#{canonical_query_string}"

# Generate the signature required by the Product Advertising API
signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), SECRET_KEY, string_to_sign)).strip()

# Generate the signed URL
request_url = "https://#{ENDPOINT}#{REQUEST_URI}?#{canonical_query_string}&Signature=#{URI.escape(signature, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}"

puts "Signed URL: \"#{request_url}\""

If one would attempt to create cart without HMAC, it would lead to an answer like this one:

"<?xml version=\"1.0\"?>\n<CartCreateErrorResponse xmlns=\"http://ecs.amazonaws.com/doc/2013-08-01/\"><Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message></Error><RequestID>b537c4f3-bfed-4c6a-95bf-*********</RequestID></CartCreateErrorResponse>"

So, my point is that -- CartCreate doesn't work without signature... at all. Maybe it would be nice thing to have code that generates it?

So I take a look at underlying jeff library:
https://github.com/hakanensari/jeff/blob/master/lib/jeff.rb

And you seem to have all the signatures in place, to interestingly enough my keys can't sign request properly through vacuum library. While Scratchpad API works without any issues.

@skatkov This is not specific to cart operations. All API requests have to be signed, which Vacuum does handle. Perhaps you're not using a valid secret access key, which is what the error message is suggesting?

@hakanensari I tested keys with scratchpad and they seem to be working. I can also do item lookups with these keys through vacuum library, but cart creation doesn't work.

here is an signed url I end with on scratchpad:

https://webservices.amazon.com/onca/xml?AWSAccessKeyId=<KEY_ID>&AssociateTag=<TAG>&Item.1.ASIN=B07GLXZMR2&Item.1.Quantity=1&Operation=CartCreate&ResponseGroup=Cart&Service=AWSECommerceService&Timestamp=2018-11-13T17%3A06%3A41.000Z&Signature=b%2F0GFyhrMeWQhnHME4lXAaFNk0bkRKvQNx0CQeqVe3s%3D

and here is signed url through vacuum:

http://webservices.amazon.com/onca/xml?AWSAccessKeyId=<KEY_ID>&AssociateTag=<TAG>&Item.0.ASIN=B01DU5HOXW&Item.0.Quantity=1&Operation=CartCreate&Service=AWSECommerceService&Signature=0gZph1HJAiaVygu5GyDjOI7TJriBs2934D6V21MUF2g=&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2018-11-13T16:29:11Z&Version=2013-08-01

I see couple of differences:

  • 'Signature' is not at the end
  • Scratchpad doesn't use 'SignatureMethod' and 'SignatureVersion' and 'Version'

Based on this CartCreate method documentation and common request parameters
https://docs.aws.amazon.com/AWSECommerceService/latest/DG/CartCreate.html
https://docs.aws.amazon.com/AWSECommerceService/latest/DG/CommonRequestParameters.html

I can assume that 'SignatureMethod' and 'SignatureVersion' are not supported.

It seems like issue is more related to jeff gem?

The API simply ignores those extra parameters.

This is what I get running a create request using a query like that in the examples folder:

{"CartCreateResponse"=>
  {"xmlns"=>"http://webservices.amazon.com/AWSECommerceService/2013-08-01",
   "OperationRequest"=>
    {"HTTPHeaders"=>{"Header"=>{"Name"=>"UserAgent", "Value"=>"Jeff/2.0.0 (Language=Ruby; Hakan-Ensaris-MacBook-Pro.local)"}},
     "RequestId"=>"33fd3888-1dbf-4087-af00-f5b9955d5be7",
     "Arguments"=>
      {"Argument"=>
        [{"Name"=>"AWSAccessKeyId", "Value"=>"foo"},
         {"Name"=>"AssociateTag", "Value"=>"bar"},
         {"Name"=>"Item.1.ASIN", "Value"=>"3791354248"},
         {"Name"=>"Item.1.Quantity", "Value"=>"1"},
         {"Name"=>"Operation", "Value"=>"CartCreate"},
         {"Name"=>"Service", "Value"=>"AWSECommerceService"},
         {"Name"=>"SignatureMethod", "Value"=>"HmacSHA256"},
         {"Name"=>"SignatureVersion", "Value"=>"2"},
         {"Name"=>"Timestamp", "Value"=>"2018-11-13T17:37:50Z"},
         {"Name"=>"Version", "Value"=>"2013-08-01"},
         {"Name"=>"Signature", "Value"=>"IB2Aus99SUFqdNfR0NlaPmJEaTxmaokuVrHeid/51iE="}]},
     "RequestProcessingTime"=>"0.15147956"},
   "Cart"=>
    {"Request"=>{"IsValid"=>"True", "CartCreateRequest"=>{"Items"=>{"Item"=>{"ASIN"=>"3791354248", "Quantity"=>"1"}}}},
     "CartId"=>"144-3493764-1473338",
     "HMAC"=>"XGk3fd7oBv3e+KErImL1Ld2qw7Y=",
     "URLEncodedHMAC"=>"XGk3fd7oBv3e%2BKErImL1Ld2qw7Y%3D",
     "PurchaseURL"=>"https://www.amazon.com/gp/cart/aws-merge.html?cart-id=144-3493764-1473338&associate-id=foo&hmac=XGk3fd7oBv3e%2BKErImL1Ld2qw7Y%3D&SubscriptionId=AKIAJA2KDABBNANGXR3Q&MergeCart=False",
     "SubTotal"=>{"Amount"=>"4800", "CurrencyCode"=>"USD", "FormattedPrice"=>"$48.00"},
     "CartItems"=>
      {"SubTotal"=>{"Amount"=>"4800", "CurrencyCode"=>"USD", "FormattedPrice"=>"$48.00"},
       "CartItem"=>
        {"CartItemId"=>"C2E387OM52BV8V",
         "ASIN"=>"3791354248",
         "SellerNickname"=>"Amazon.com",
         "Quantity"=>"1",
         "Title"=>"Andy Warhol: The Complete Commissioned Record Covers",
         "ProductGroup"=>"Book",
         "Price"=>{"Amount"=>"4800", "CurrencyCode"=>"USD", "FormattedPrice"=>"$48.00"},
         "ItemTotal"=>{"Amount"=>"4800", "CurrencyCode"=>"USD", "FormattedPrice"=>"$48.00"}}}}}}

I'm at a loss as to what the issue could be if other queries run fine. The above certainly works.

I think issue is with 'Signature' parameter in url, in a non-working version Signature is somewhere in a middle of URI.

I'll play around tomorrow.

I fixed it, to my surprise.

          params.merge(
-          :Operation => :CartCreate,
+          'Operation' => 'CartCreate'
          )

If anyone ran into similar issue, make sure that your not passing Symbols to query hash :)

stale commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.