Amazon has a handy-dandy tool for testing requests and confirming that you are signing requests correctly they call a 'scratchpad'. You hand it an unsigned request, put in your authentication information, hit submit, and it spits out a signed request. Either it or my code is broken, and logic suggests that it's my code, but nothing about this makes sense. Maybe someone here can see something that I'm not.
If I hand the scratchpad an unsigned request like
http://webservices.amazon.com/onca/xml?AWSAccessKeyId=ME&AssociateTag=ME&Keywords=bbq&Operation=ItemSearch&ResponseGroup=ItemAttributes&SearchIndex=All&Service=AWSECommerceService
I get back
http://webservices.amazon.com/onca/xml?AWSAccessKeyId=ME&AssociateTag=ME&Keywords=bbq&Operation=ItemSearch&ResponseGroup=ItemAttributes&SearchIndex=All&Service=AWSECommerceService&Timestamp=2017-07-16T15%3A14%3A09.000Z&Signature=4oLDpXEdZ%2BEEPBOOPIMAROBOTiALFPPeICbs%3D
If I make a request to that URL (that I got from AMZN's tool), I get
Value 2017-07-16T15%3A14%3A09.000Z for parameter Timestamp is invalid. Reason: Must be in ISO8601 format.
If I manually un-url-encode the timestamp to 2017-07-16T15:14:09.000Z
then it seems to get past that, only to fail with the dreaded SignatureDoesNotMatch
.
BUT
If I make my own signed message using that query string from the signed url above, and the url-encoded timestamp, the signatures match! Which means their backend is using the url-encoded timestamp to compute the signature, but any request by me with a timestamp, url-encoded or not, gives me the "that's not iso8601" error. All the url-encoding is doing is substituting %3A
for :
- not a complex process.
I've confirmed that the locale of the server this is running on is utf-8, and that I'm using application/x-www-form-urlencoded; charset=utf-8
as the content-type of the requests, as well as confirmed that the system clock is synced with an authoritative ntp server.
Hopefully someone has seen this or something like it before.
The Drakma client automatically url-encodes parameters, and it turns the time was encoded twice. It also encodes parameters even if you don't pass them as a parameter list, but directly in the uri. In order to keep to URI unmodified, you have to use :preserve-uri t
, as explained in the documentation:
If preserve-uri is not NIL, the given uri will not be processed. This means that the uri will be sent as-is to the remote server and it is the responsibility of the client to make sure that all parameters are encoded properly. Note that if this parameter is given, and the request is not a POST with a content-type of `multipart/form-data', parameters will not be used.