amz-tools / amazon-sp-api

Amazon Selling Partner API Client
MIT License
231 stars 119 forks source link

Problem with the request signature #136

Closed benisdp closed 11 months ago

benisdp commented 2 years ago

I am trying to make a request to see my inventory.

let inventory = await sellingPartnerMain.callAPI({
          operation: "getInventorySummaries",
          endpoint: "fbaInventory",
          query: {
            MarketplaceId: "ATVPDKIKX0DER",
            granularityType: "Marketplace",
            granularityId: "ATVPDKIKX0DER",
          },

When I make the request, I am getting this error:

CustomError: 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.The Canonical String for this request should have been'GET/fba/inventory/v1/summariesMarketplaceId=ATVPDKIKX0DER&granularityId=ATVPDKIKX0DER&granularityType=Marketplacehost:sellingpartnerapi-na.amazon.comuser-agent:amazon-sp-api/0.7.4 (Language=Node.js/v16.15.0; Platform=Darwin/21.2.0)x-amz-access-token:Atza|IwEBIFmEu5LQ-vF6aWcmkr8AfFnKLnBSEsd3ARevKTZul3qmsNVwlMNHKPxCDubNDbHabfEJycszIG9_H9K6phsPvbzn9xut-fNaN7h2jZW5aCdDKeEa26Q8pCnEtseM4M-xUoa1wIPRijnDAzCeosGz17lS_XurUf42WL7je-8fm44KzhQQoHUwv2FJFIrwG5Zso1sQEepry0tWgNxZthQI_Mj8aXvFzhB2Db01hKzMmBulvEtW7ngCefUi-ulKLI7CYfYVJrhDp9BwcMaC4JpeTKCNEu0QsgeLoVOx-lY9vkUJJTmsZ-dnKus0okvt09IYxz8x-amz-date:20220602T003644Zhost;user-agent;x-amz-access-token;x-amz-datee3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'The String-to-Sign should have been'AWS4-HMAC-SHA25620220602T003644Z20220602/us-east-1/execute-api/aws4_request71745266a381391d6f957f3eb590c1f4f21dd31a6fee269cb011f884dd4b0f7d'

Here is the code I am using to create the sellingPartner Instance:

 app.get("/sp-api", async (req, res) => {
    (async () => {
      try {
        const shop = req.cookies.shopOrigin;
        const user = await User.findOne({ shopOrigin: shop });
        console.log("this is the user", user);
        let sellingPartnerMain = new SellingPartnerAPI({
          region: "na", // The region to use for the SP-API endpoints ("eu", "na" or "fe")
          refresh_token: user.amazonRefreshToken,
          access_token: user.amazonAccessToken,
          // role_credentials: {
          //   id: "<TEMPORARY_ROLE_ACCESS_ID>",
          //   secret: "<TEMPORARY_ROLE_ACCESS_SECRET>",
          //   security_token: "<TEMPORARY_ROLE_SECURITY_TOKEN>",
          // },
          credentials: {
            SELLING_PARTNER_APP_CLIENT_ID:
              "amzn1.sp.solution.01484171-26e9-462b-ba17-09cee9dda670",
            SELLING_PARTNER_APP_CLIENT_SECRET:
              "eb3a8a78c4e69e920fa44e15b0957099b4d323002152a63d4b6cce416c77fa9d",
            AWS_ACCESS_KEY_ID: "AKIAQMQDPPF376QSBN4Y",
            AWS_SECRET_ACCESS_KEY: "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",
            AWS_SELLING_PARTNER_ROLE:
              "arn:aws:iam::026850851191:role/dojoappsapiuserwriteread",
          },
          // options: {
          //   use_sandbox: true,
          // },
        });

Am i missing something in the credentials?

Also to note, the amazon app that is authorized is in draft status, but I'm not sure if that should affect this. I feel this is a problem with the GET request I am sending.

-Beni

benisdp commented 2 years ago

Do i have to use STS temporary credentials instead since I am doing this from a role account?

benisdp commented 2 years ago

And also, do you know if requests are rejected if the app is in draft mode on Seller Central?

Landsailor commented 2 years ago

Do i have to use STS temporary credentials instead since I am doing this from a role account?

As I understand it, the standard way of accessing SP-API is by using temporary credentials generated by STS so you can assume the role that's allowed to access the SP-API app.

Are you logged in as the single Role associated with the app? It seems unlikely since that role is created as part of the setup process if you followed the configuration instructions provided by Amazon. The fact that those temporary credentials expire every hour is another layer of security.

Check which role the app is using at https://sellercentral.amazon.com/sellingpartner/developerconsole?

You should be able to at least access the sandbox endpoints while in Draft status.

(AFAIK, anyway. I am far, far from an expert since I'm still implementing it, as well.)

benisdp commented 2 years ago

@Landsailor Thanks for the response. Amazon's documentation is very confusing, so I appreciate any sort of direction. I am using a role account that is connected to a user account. I tried using the user account (as i figure it would have all credentials), but the sellingPartner seems to want a role user.

Moving on with this issue, I'm not sure where I can get these temporary credentials. I've tried manually refreshing them from the sellingPartner, but keep getting an error.

heres the code im using to try and get the sts temporary credentials

let sellingPartner = new SellingPartnerAPI({
          region: "na",
          refresh_token: user.amazonRefreshToken,
          options: {
            auto_request_tokens: false,
          },
        });

        console.log("selling partner", sellingPartner);

        await sellingPartner.refreshAccessToken();
        await sellingPartner.refreshRoleCredentials();

and a picture of my roles in seller central

Screen Shot 2022-06-03 at 1 51 11 PM

In seller central, the role user is assuming the role from the "user" user

benisdp commented 2 years ago

Some more errors i get when i try (this is the error log on server side)

selling partner SellingPartner {
┃   _region: 'na',
┃   _refresh_token: 'Atzr|IwEBIBlxKUdqHRfMjwD3ma2kvYzgz9CnZ2mDaPxzdhM0ZsXfr7ZVPIexEqCWzsiiB75HAwPwlDwPK9V6zSBrHoxZj4eXkB8XUjpR1P0aiiC-_NTe3UgNvPOBdlrg6fpUVM-RkI8l-LBTGMx3M-cqaJMnd-aYC6UXjuZ8kw3QLQVSE5iGsPUT-vTiXzihtZwJpeSVG63Bt_d6CO42ofWhvL-0a0Hl_B1q_6gGtii__A5GxFiRRurAe07IOXj75AnkehqzunKf2oIh6s5qoYwYE16HVEh_pQhH4-pp90kOfpu6VnMTwh9G4WrBBhG_EIRJNm40eu4',
┃   _access_token: undefined,
┃   _role_credentials: undefined,
┃   _grantless_tokens: {},
┃   _options: {
┃     auto_request_tokens: true,
┃     auto_request_throttled: true,
┃     use_sandbox: false,
┃     only_grantless_operations: false,
┃     version_fallback: true,
┃     user_agent: 'amazon-sp-api/0.7.4 (Language=Node.js/v16.15.0; Platform=Darwin/21.2.0)',
┃     debug_log: false
┃   },
┃   _endpoints_versions: {},
┃   _credentials: {
┃     app_client: {
┃       id: 'amzn1.application-oa2-client.0a32cf84b269483cb7e970a763f6395c',
┃       secret: 'b9bb5c3eacbcbfdbdb858d1464f5f1a7cc6c8610abded0014f1560ad84db6c00'
┃     },
┃     aws_user: {
┃       id: 'AKIAQMQDPPF376QSBN4Y',
┃       secret: 'jTgQOz2AA1eiChatmodZRNdDyBwqx+/oxwGs1AzT',
┃       role: 'dojoappsapiuserwriteread'
┃     }
┃   },
┃   _xml_parser: XMLParser {
┃     externalEntities: {},
┃     options: {
┃       preserveOrder: false,
┃       attributeNamePrefix: '@_',
┃       attributesGroupName: false,
┃       textNodeName: '#text',
┃       ignoreAttributes: true,
┃       removeNSPrefix: false,
┃       allowBooleanAttributes: false,
┃       parseTagValue: true,
┃       parseAttributeValue: false,
┃       trimValues: true,
┃       cdataPropName: false,
┃       numberParseOptions: [Object],
┃       tagValueProcessor: [Function: tagValueProcessor],
┃       attributeValueProcessor: [Function: attributeValueProcessor],
┃       stopNodes: [],
┃       alwaysCreateTextNode: false,
┃       isArray: [Function: isArray],
┃       commentPropName: false,
┃       unpairedTags: [],
┃       processEntities: true,
┃       htmlEntities: false,
┃       ignoreDeclaration: false,
┃       ignorePiTags: false
┃     }
┃   }
┃ }
┃ CustomError: Client authentication failed
┃     at SellingPartner.refreshAccessToken (/Users/beni/Documents/Shopify_Apps/FBA-Connector-App/node_modules/amazon-sp-api/lib/SellingPartner.js:453:13)
┃     at processTicksAndRejections (node:internal/process/task_queues:96:5)
┃     at async file:///Users/beni/Documents/Shopify_Apps/FBA-Connector-App/server/index.js:176:9 {
┃   code: 'invalid_client',
┃   type: 'error'
┃ }
Landsailor commented 2 years ago

@benisdp Nothing about SP-API is intuitive. It might be for AWS experts, but for most people who don't spend the majority of their day dealing with it, it's not. I'm in the process of integrating it now and am far from done, but I'm happy to share what I've figured out. (Be sure to change your credentials for everything since they're exposed in the text.)

If you're not using Postman to work through this, please do. It will help immensely with keeping everything straight in your head as well as ensure you have the proper permissions before trying to code it all.

STS AssumeRole You need to use the Security Token Service's AssumeRole to GET your temporary credentials for the rest of SP-API. You're going to use your current login role's credentials to assume the role that is listed in your app and get another set of temporary credentials. You will use those credentials to authenticate your requests and generate the signatures.

Screen Shot 2022-06-03 at 11 27 27 PM

Exchange Refresh Token for Access Token This is a POST to https://api.amazon.com/auth/o2/token

There are NO credentials in the Authorization section. Your app's LWA credentials (from the popover on the screen) are sent in the header, along with the refresh_token as seen here:

Screen Shot 2022-06-03 at 11 42 53 PM
nothinman commented 1 year ago

@benisdp @Landsailor did you guys get it to work in the end? I'm having the same problem with fetching inventory; other things seem to work. Does not seem to be an issue with IAM, but rather calculating the GET string. Same as with MWS. PITA.

amz-tools commented 11 months ago

Closed as wontfix/invalid as signing is not necessary anymore.