amzn / selling-partner-api-models

This repository contains OpenAPI models for developers to use when developing software to call Selling Partner APIs.
Apache License 2.0
609 stars 732 forks source link

Denied when swapping SPAPI Auth Code for Refresh/Access Token #739

Closed alex-lehan-bloom closed 4 years ago

alex-lehan-bloom commented 4 years ago

I'm new to SPAPI and have been able to get most things working. I am even able to redirect the user from our app to Amazon Seller Central to confirm our app's access to that user's Amazon Seller account. When the user confirms, Amazon Seller Central redirects back to our app with the spapi_oauth_code in the redirect URL, just like the docs say.

Everything is working fine up until this point. But then, when I try to use the spapi_oauth_code to get a refresh / access token from Login with Amazon, my request always fails with an "invalid grant type" error, even though my grant type is "authorization_code", just like the doc says. I've read issue amzn/selling-partner-api-models#699 and issue amzn/selling-partner-api-models#713 , but I don't think my configuration is the issue. At least, I've checked the configuration against the documentation several times.

Here's what I've done so far. Am I doing something wrong?

My IAM Policy JSON, which is attached to my IAM Role:


{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:*:*:*"
        }
    ]
}

My IAM Role is attached to my IAM User, along with STS. In the code below, I replaced my IAM Role ID with 0s.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::000000000:role/SellingPartnerAPIRole"
        }
    ]
}

Up to now everything seems okay. I'm doing the Website authorization. And the user is able to give us consent to their Amazon Seller Account. On the redirect, I get back this:

https://test.com/accounts?&spapi_oauth_code=ANLTDkLEBxVfIePEaFxV&state=b221583e-373c-4104-9176-c6279adc5aa5&selling_partner_id=45554445445

Again, all seems okay. Now here is where the issue is. I do the request to swap the auth code above for an access token. The docs say to use query parameters so that's what I did.

    const test = await fetch(
      `https://api.amazon.com/auth/o2/token?code=${spapiCode}&client_id=${clientId}&client_secret=${secret}&grant_type=authorization_code&redirect_uri=https://test.com/accounts`,
     {
        method: "post",
      headers: {
       "Content-Type": "application/x-www-form-urlencoded",
      },
    }
    );

According to the docs, this is okay. If I remove one of the parameters I get the excepted response, such as "code missing" or "client_id missing". But if I leave all the query parameters, I actually get a 500 back from the server.

If I change the query parameters to req body parameters, then I get back the:

{
  "errors": [
    {
      "message": "Access to requested resource is denied.",
      "code": "MissingAuthenticationToken"
    }
  ]
}
charliecode commented 4 years ago

@alex-lehan-bloom When you redirect the user to Seller Central what does your URL look like, are you using the version=beta param on it?

alex-lehan-bloom commented 4 years ago

Wow, @charliecode , you're amazing. You seriously have the quickest replies.

Yeah, I do have the version=beta param on it.

Here's what I open. I just replaced the app ID with 0s. Does anything look wrong with this?



 window.open(
              `https://sellercentral.amazon.com/apps/authorize/consent?application_id=amzn1.sellerapps.app.000000000000000&state=none&version=beta`
            );
rogersv commented 4 years ago

Hi, did you use a real redirect url? It is possible that amazon gets an internal error if the url is invalid or not using https.

charliecode commented 4 years ago

@alex-lehan-bloom What I'm thinking is this. Because you have the version=beta in the url, this means this is not a production auth call but rather a test auth call. Therefore the spapi_oauth_code returned back to you, is a dummy. So when you're trying to get the refresh token so you can get your actual access token the system sees your dummy spapi_oauth_code and doesn't allow it to go through. Everything in the LWA request looks similar to what I did. I don't have time to test this out right now but I will later today. I could be wrong on this, it's just a theory until I test it. I did this like 2 weeks ago so I honestly don't remember if my LWA request went through with the dummy spapi_oauth_code.

I also went straight to getting the auth working. But then I realized it was better to get the test auth flow working first and then move on to a Self Authorization so I could start working on the API. The reason for this is because you cannot remove the version=beta until your app is published and approved by Amazon. You can read about that in issue amzn/selling-partner-api-models#726. I'll test later and let you know unless I see you get if figured out before then.

alex-lehan-bloom commented 4 years ago

@rogersv , good question. I did use a real redirect URL I believe, at least I'm using the same one that I have in my Amazon Seller Central application. I think it must be ok because it redirects back to my app using that URL after the user gives consent to our app in their Amazon Seller Central account.

@charliecode , thank you! That would be amazing if that is the issue. Then I could move on to making SPAPI calls and use Self Authorization until I actually publish my app.

charliecode commented 4 years ago

@alex-lehan-bloom So I just made the request to test it with the dummy spapi_oauth_code. The error I got is below. Again, there could be something wrong with my request, although I do not think so. I have not tested this a ton and this is just a theory but in the event yours looks similar maybe I am right about the dummy code being the issue. Would be nice if someone from Amazon would chime in to verify because the docs only mention the test auth flow working from the seller central side. They do not mention whether the flow will continue to work when sending the dummy spapi_oauth_code to LWA to get the refresh and access tokens. And you can't really say your entire test auth flow is finished and tested until you can verify you've been given a dummy refresh and access token from LWA.

{ "error_description": "The request has an invalid grant parameter : code", "error": "invalid_grant" }

UPDATE: SEE COMMENT BELOW

alex-lehan-bloom commented 4 years ago

@charliecode , that's exactly the same issue I hit. Thank you! I'll working on getting my app published and approved by Amazon so I can confirm.

alex-lehan-bloom commented 4 years ago

Closing for now.

petergzli commented 3 years ago

Hey @charliecode @alex-lehan-bloom any developments with this? I just started and have reached this step and unable to proceed { "error_description": "The request has an invalid grant parameter : code", "error": "invalid_grant" }

Am I stuck testing on self-grant option only?

alex-lehan-bloom commented 3 years ago

@petergzli , it looks like it. That's the same error I got.

charliecode commented 3 years ago

Hello @petergzli. I believe so. I do believe the issue is with the "dummy code" as mentioned above. Nobody has chimed in from Amazon yet, so until they do we won't 100% know. But, I'd say what we came up with is probably correct. Would definitely be nice if Amazon could add more to the documentation on this. And as I said before, it seems being able to do the entire flow, including using the spapi_oauth_code to get dummy LWA data would be a nice addition to ensure our entire flow is working correctly. Until then I would recommend doing the self authorization. Not sure if you read amzn/selling-partner-api-models#726 yet or not, but it helps bring this all together.

UPDATE: SEE COMMENT BELOW

charliecode commented 3 years ago

To take it a step further it would also be nice to then use that dummy LWA access and refresh tokens to at least be able to make sandbox requests. Then you would be ensured that your entire oauth flow works from beginning to end.

petergzli commented 3 years ago

@charliecode I agree it would be incredibly helpful for development else anything can go wrong once we submit for review and they try out the same flow in production servers...

Btw you are fantastic, thank you for your constant contribution to all these threads. You are truly appreciated.

charliecode commented 3 years ago

UPDATE ON PREVIOUS COMMENTS:

When trying to swap the spapi_oauth_code via LWA's https://api.amazon.com/auth/o2/token endpoint, you should be able to successfully receive the response Amazon mentions in their documentation even if your spapi_oauth_code came from a test (draft state) request. Namely the access_token, token_type, expires_in and refresh_token. Previously some of us were getting errors when attempting to do this. This is not currently the case. So, if you're currently getting errors when trying to do this, it's more than likely an issue with your request.

Khungersumit commented 3 years ago

Hello Everyone So after reading the entire chat above I have another question while working with SP-API. Can anyone please have a look, I would be really thankful to you :)

After exchanging the aut_code with the LWA refresh token, I got the following things access_token | refresh_token | token_type | expires_in

Then I tried to call (ref : here)

https://sellingpartnerapi-na.amazon.com/orders/v0/orders with headers x-amz-access-token : got in the previous step x-amz-date and user-agent

Now when I HIT the request I get the following response "message": "Access to requested resource is denied.", "code": "MissingAuthenticationToken"

Is there anything I am doing wrong? Please share your thoughts Thanks in advance to all 😊

EddieLongStockings commented 3 years ago

Hi same problem. Using Self Authorization as app is still in draft status: https://github.com/amzn/selling-partner-api-docs/blob/main/guides/developer-guide/SellingPartnerApiDeveloperGuide.md#self-authorization

mehmetuygun commented 3 years ago

Hello Everyone So after reading the entire chat above I have another question while working with SP-API. Can anyone please have a look, I would be really thankful to you :)

After exchanging the aut_code with the LWA refresh token, I got the following things access_token | refresh_token | token_type | expires_in

Then I tried to call (ref : here)

https://sellingpartnerapi-na.amazon.com/orders/v0/orders with headers x-amz-access-token : got in the previous step x-amz-date and user-agent

Now when I HIT the request I get the following response "message": "Access to requested resource is denied.", "code": "MissingAuthenticationToken"

Is there anything I am doing wrong? Please share your thoughts Thanks in advance to all 😊

Did you find any solution for this :/?

ezpinsky commented 3 years ago

UPDATE ON PREVIOUS COMMENTS:

When trying to swap the spapi_oauth_code via LWA's https://api.amazon.com/auth/o2/token endpoint, you should be able to successfully receive the response Amazon mentions in their documentation even if your spapi_oauth_code came from a test (draft state) request. Namely the access_token, token_type, expires_in, and refresh_token. Previously some of us were getting errors when attempting to do this. This is not currently the case. So, if you're currently getting errors when trying to do this, it's more than likely an issue with your request.

It seems that this is not correct because amazon differentiates between private (unlisted/draft) and public (listed) in the docs here https://github.com/amzn/selling-partner-api-models/issues/739 with regard to authorization workflow. It is implied that the only time you can use the OAuth workflow is for a Public application (i.e. listed on the AppStore). SelfAuthorization is the only way to use a Private (unlisted) application. This seems to be the reason that everyone is getting the 500 error with a correct request.

yuri-lonesomelabs commented 3 years ago

I'm getting 500 when trying to get a refresh token on a draft app.

Can anyone share a request structure that actually worked when trying to exchange spapi_oauth_code for a refresh token on a drafted app? Or at least confirm that it's possible/not possible.

If not possible, how are you guys testing the flow before going public?

EDIT: Getting these headers with the 500 error: {'Server': 'Server', 'Date': 'Fri, 11 Jun 2021 08:31:49 GMT', 'Content-Type': 'application/json;charset=UTF-8', 'Content-Length': '2', 'Connection': 'keep-alive', 'x-amz-rid': '5T2RS7ZNSSS8ME94NRA3', 'x-amzn-RequestId': '81715f72-669b-4801-882e-c281d0ccaed4', 'X-Amz-Date': 'Fri, 11 Jun 2021 08:31:49 GMT', 'x-amzn-ErrorType': 'InternalFailure:http://internal.amazon.com/coral/com.amazon.coral.service/', 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Vary': 'Content-Type,Accept-Encoding,X-Amzn-CDN-Cache,X-Amzn-AX-Treatment,User-Agent', 'Permissions-Policy': 'interest-cohort=()'}

ezpinsky commented 3 years ago

@yuri-lonesomelabs ~~It is not possible and that fact is barely implied by the awful documentation. See my earlier comment below, but in short you will have to apply for the app to be listed and get approved for you to test this request. Your app does not have to be complete for you to apply. May as well apply now because it can take some time (around 4 business days) to get approved and then they take another 10 business days to translate your app listing info before it gets listed. Oh and don't forget to remove the version=beta from the OAuth link when you do finally get listed.~~

See my comment below correcting my this incorrect comment

It seems that this is not correct because amazon differentiates between private (unlisted/draft) and public (listed) in the docs here https://github.com/amzn/selling-partner-api-models/issues/739 with regard to authorization workflow. It is implied that the only time you can use the OAuth workflow is for a Public application (i.e. listed on the AppStore). SelfAuthorization is the only way to use a Private (unlisted) application. This seems to be the reason that everyone is getting a 500 error. Of course... amazon has terrible error handling with barely any info.

yuri-lonesomelabs commented 3 years ago

@ezpinsky so you just have an unfinished app published on the marketplace? Is there no other way to test the app before that, at least the APIs with some sort of dummy data?

ezpinsky commented 3 years ago

@ezpinsky so you just have an unfinished app published on the marketplace? Is there no other way to test the app before that, at least the APIs with some sort of dummy data?

Yup, we have an incomplete app that is published to the marketplace. You can put the price of the app as "Contact seller for pricing" so that no one can try your app without first contacting you.

You can test all the APIs with Self Authorization. The only thing you can't test until listed is swapping the SP-API auth code for refresh token. Pretty annoying that you cant really test it until listed but in some ways it makes sense. They only want you to be able to authorize sellers to your app if they have approved it and made sure it's not harmful to sellers I guess. I hope this helps.

The above is wrong.

ezpinsky commented 3 years ago

@yuri-lonesomelabs Turns out we weren't making the request correctly. It does work in draft mode. Our mistake was twofold.

  1. We were putting all the parameters for the request as a query on the url, because that's what the docs say to do...

The call must include the following query parameters...

A query parameter by definition goes on the end of the url... But of course this is wrong because the docs are terrible. You have to put the client_id, client_secret, code, and grant_type into the body of your request.

  1. Didn't realize that the the LWA exchange is actually a grantless operation and doesn't need to be signed or anything. It doesn't say this anywhere in the docs that I could find though. It does mention getAuthorizationCode is a grantless operation but not sure what that is and it is not clear if that refers to the LWA Exhange.
SouravPal95 commented 3 years ago

@ezpinsky Thank you so much for that solution. Documentations need to be more robust. Till then the community is all we got. 🧠🧠

xbaha commented 3 years ago

Why there is a return URL? if the reply is Json???

agptaa commented 2 years ago

Hi I am facing 401 issue while making a post request for a refresh token. I am sending following as the request body - { "grant_type": "authorization_code", "code": spapi_oauth_code_fromURL, // auth code coming from the URL params "redirect_uri": , "client_id": process.env.CLIENT_ID, // from LWA credentials "client_secret": process.env.CLIENT_SECRET, // from LWA credentials } Can anyone suggest what might be wrong ?

jilliansd commented 2 years ago

Hi,

I am facing a 400 error when trying to make a post request to get a refresh token from the sp_oauth token:=, am sending this via postman:

var axios = require('axios'); var qs = require('qs'); var data = qs.stringify({ 'grant_type': 'authorization_code', 'code': 'ANnGBdMPnXAlrMyfUdwU', 'client_id': 'amzn1.application-oa2-client.cf71c857f2fd4f2c9688516xxxxxxx', 'client_secret': 'bba375434456e9917fcd5539c4324cc0e3182cccb9f1694ce3c63beexxxxxxx', 'redirect_uri': 'https://dashboard.aaa.com/api/amzn/redirect' });

var config = { method: 'post', url: 'https://api.amazon.com/auth/o2/token', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }, data : data };

axios(config) .then(function (response) { console.log(JSON.stringify(response.data)); }) .catch(function (error) { console.log(error); });

I am getting back: 400 Bad request:

{ "error_index": "nk74kjk1VvJt-2-6787LegAAAAAAAAABQQnHaBVBp4XYo69U3I-OIQGvRBEbNoklBlrU5y0PNK7xvv16-uq0Zs2BAGrJ-mt4ugp4Ic9jkShoBmEHb-iFKGpkuzBrhJ3_CiqKzbAQn2OK7-zCciNGAOUZHqZTmVXtjLjfsZl1s0m4US8dqE8-QcNkOMua-S-23E4wnldXOcJrDCcCrPMZZk5GM6BKLDKoeS2ZEVj7sLQg1FjpzAa7rQ==", "error_description": "Malformed request", "error": "invalid_request" }

Anyone had success exchanging sp_oauth code for a refresh token? Please share any pointers on how to fix this problem. The request format seems to be a problem but I could not figure it out.

sofian-ysf commented 2 years ago

Really helpful feed, I'm using React JS and am getting stuck on the step just prior to this (retrieving the spapi_oauth_code from the url). The workflow authorises correctly and the query parameters show instantaneously then it redirects back to my webpage. The parameters are available when I check the chrome console network tab. How am I supposed to retrieve this to use in the following post request?

albinsopaj commented 1 year ago

Really helpful feed, I'm using React JS and am getting stuck on the step just prior to this (retrieving the spapi_oauth_code from the url). The workflow authorises correctly and the query parameters show instantaneously then it redirects back to my webpage. The parameters are available when I check the chrome console network tab. How am I supposed to retrieve this to use in the following post request?

Hey. Did you find out how to extract (parse, decode) the spapi_oauth_code from the URL? I'm stuck at this, and I'd really appreciate some help.

[ SOLVED ]

albinsopaj commented 1 year ago

into the body of your request

Yep. The solution that you proposed worked for me. I created the following array.

$data = [
      'grant_type' => 'authorization_code',
      'code' => '*******************************************',
      'redirect_uri' => '*******************************************',
      'client_id' => '************************************************',
      'client_secret' => '***********************************************'
];

I sent it via a POST request, and as a response I got back the following:

{
    "access_token": "*************************************",
    "refresh_token": "*****************************************",
    "token_type": "bearer",
    "expires_in": 3600
}

All seems to be working correctly.