BenNathan
BenNathan

Reputation: 11

Sign SP-API request in Google Apps Script

I'm trying to call the Amazon SP-API from Google Apps Script. I was able to retrieve the access-token. However when trying call the I attempted to follow the advice on this post: Google Apps Script: Getting Orders from Amazon Selling Partner API (Signing Requests)

There seems to be something wrong with the way the signature is being calculated.

(I am able to make this call in Postman.)

However in Goolge Apps Script I'm getting an InvalidSignature response.

This is my code:

function getAsins() {
  const hex = bytes => bytes.map(byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
  const digestToHex = data => hex(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, data));
  const toBytes = data => Utilities.newBlob(data).getBytes();

  //Credential variables
  var access_token = AccessToken();
  const ACCESS_ID = 'AKIEXAMPLEEXAMPLEW5';
  const ACCESS_KEY = 'bh8EXAMPLEEXAMPLEWw5SA/EXAMPLE+5EXAMPLEP';
  const marketplaceId = 'ATVPDKIKX0DER';

  //Time variables
  var currentDate = new Date();
  var isoDate = currentDate.toISOString();
  var isoString = isoDate.replace(/-/g, "").replace(/:/g, "").replace(/(\.\d{3})/, "");
  var yearMonthDay = Utilities.formatDate(currentDate, 'GTM-4', 'yyyyMMdd');

  //API variables
  var end_point = 'https://sellingpartnerapi-na.amazon.com';
  var aws_region = "us-east-1";
  var service = "execute-api";
  var termination_string = "aws4_request";

  //CanonicalRequest components:
  var asin = 'B07X6C9RMF';
  var httpRequestMethod = 'GET';
  var canonicalURI = '/catalog/2022-04-01/items/' + asin;
  var canonicalQueryString = '?marketplaceIds=' + marketplaceId;
  var canonicalheaders = 'host:' + "sellingpartnerapi-na.amazon.com" + '\n' + 'x-amz-access-token:' + access_token + '\n' + 'x-amz-date:' + isoDate;
  var signedheaders = 'host;x-amz-access-token;x-amz-date'; //;user-agent
  var requestPayloadHashed = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, "");//NEW
  requestPayloadHashed = requestPayloadHashed.map(function (e) { return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2) }).join("");//NEW

  //Building the canonical request
  var canonical_string = httpRequestMethod + '\n' + canonicalURI + '\n' + "MarketplaceIds=" + marketplaceId + '\n' + canonicalheaders + '\n\n' + signedheaders + '\n' + requestPayloadHashed;//UPDATED
  var canonical_signature = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, canonical_string);
  canonical_request = canonical_signature.map(function (e) { return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2) }).join("");
  var credential_scope = yearMonthDay + '/' + aws_region + '/' + service + '/' + termination_string;
  var string_to_sign = "AWS4-HMAC-SHA256" + '\n' + isoString + '\n' + credential_scope + '\n' + canonical_request;
  var kSecret = ACCESS_KEY;
  var kDate = Utilities.computeHmacSha256Signature(yearMonthDay, "AWS4" + kSecret);
  var kRegion = Utilities.computeHmacSha256Signature(toBytes(aws_region), kDate);
  var kService = Utilities.computeHmacSha256Signature(toBytes(service), kRegion);
  var kSigning = Utilities.computeHmacSha256Signature(toBytes(termination_string), kService);
  var signature = hex(Utilities.computeHmacSha256Signature(toBytes(string_to_sign), kSigning));
  Logger.log('signature: ' + signature)

  var options = {
    'method': 'GET',
    'headers': {
      'x-amz-access-token': access_token,
      'x-amz-date': isoDate,
      'Authorization': 'AWS4-HMAC-SHA256 Credential=' + ACCESS_ID + '/' + credential_scope + ', SignedHeaders=' + signedheaders + ', Signature=' + signature,
    },
    'muteHttpExceptions': true
  }

  var asinData = UrlFetchApp.fetch(end_point + canonicalURI + canonicalQueryString, options);
  Logger.log(asinData);
}

This is the response I'm getting:

    {
  "errors": [
    {
      "message": "The request signature we calculated does not match the signature you provided.
....
....
The String-to-Sign should have been
'AWS4-HMAC-SHA256
20220701T185142Z
20220701/us-east-1/execute-api/aws4_request
9a5fa583759a5ff04d7ce67d01fcf7157e9f8a58c4fbbdd69f91cb7a816f5650'
",
     "code": "InvalidSignature"
    }
  ]
}

Any help sorting this out would be greatly appreciated!

Upvotes: 1

Views: 570

Answers (1)

sheila
sheila

Reputation: 36

I'm only starting on this but I think the Canonical Query String does not include the question mark.

Upvotes: 0

Related Questions