Unable to receive refresh token for LWA


#1

Hi there,

as a developer, I have a persisting issue with LWA and I’m running out of ideas. Maybe some of you could shed some light, what I’m missing.

In short:

  • Trying to use LWA to get access to the newly launched Selling Partner API
    • Use Case: Building an app for fetching inventory listing for a seller
  • Already registered our developer account with Amazon
    • Correctly obtained Amazon MWS credentials
  • Created application in Developer Central
    • Configured using both old MWS and new SP API
    • Configured all necessary roles (e.g. for inventory listing)
    • Got through Amazon review process successfully (received email with confirmation)
    • Correctly obtained credentials for Login With Amazon (e.g. client id)
    • Application currently is in DRAFT state
  • Consulted Developer Guide for SP API (link to Github)
    • Every step done to register app and setup system
  • Successfully authenticated a seller by using the ‘Website Flow’ (see Github docs)
    • Used the parameter ‘version=beta’ to be able testing my app in draft state
    • Tried with the official JavaScript SDK
  • Regarding JavaScript integration
    • Integrated the JS SDK, put a simple button to click
    • Set correct client id, set scope ‘profile’
    • Received the LWA auth code from JS SDK successfully
  • Tried exchanging sellers LWA auth code with a refresh token
    • According to the GitHub docs from above (step 4 of ’ Website workflow’)
    • The first POST call results in an HTTP 405 Not Allowed
    • Used grant_type ‘authorization_code’
    • Used the sellers LWA auth code
    • Also sent the client_id and client_secret from my Amazon application (mentioned above)

Searching for this message via Google this forum didn’t help me. Some speak of the need to whitelist my application, but to be honest I cannot tell where and how.

The developer account has just 1 application configured, and from the Developer Central POV it looks okay.

If you need additional information like parts of my client or app ID, don’t hesitate asking. My problem may or may not be a small issue I’m running into, appreciating any kind of hint or help.

fyi: I got sent here after creating a topic in the forums.developer.amazon.com
If I’m wrong here, please let me know where to find information about the rather new SP API.


#2

I’m in a same spot as you. Looking for answers on how to move forward. But I’m using HTTP API not Java. And I actually have a refresh tocken by self-authorization. But I still have HTTP 405 Not Allowed

    POST /auth/o2/token HTTP/1.1
    Host: https://api.amazon.com
    Content-Type: application/x-www-form-urlencoded;charset=UTF-8
    grant_type=refresh_token
    &refresh_token=Atzr|... 
    &client_id=amzn1.application-oa2-client....
    &client_secret=secrtet

#3

I did it. Here is my PHP code:

require '../vendor/autoload.php';

use Aws\Signature\SignatureV4;
use Aws\Credentials\Credentials;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;

$options = [
    'host' => 'https://api.amazon.com',
    'region' => 'us-east-1',
    'service' => 'execute-api',
    // @see https://github.com/amzn/selling-partner-api-docs/blob/main/guides/developer-guide/SellingPartnerApiDeveloperGuide.md#step-1-create-an-aws-account
    'aws_user_key' => 'AKIXXXXXXXXXXXXXX', // @see https://console.aws.amazon.com/iam/home?#/users/{user}
    'aws_user_secret' => 'XXXXXXXXX',
    // @see https://github.com/amzn/selling-partner-api-docs/blob/main/guides/developer-guide/SellingPartnerApiDeveloperGuide.md#viewing-your-developer-information
    // @see https://sellercentral.amazon.com/sellingpartner/developerconsole/
    'app_client_id' => 'amzn1.application-oa2-client.XXXXXXXXXX',
    'app_client_secret' => 'XXXXXXXXXXXXXXX',
];

$credentials = new Credentials($options['aws_user_key'], $options['aws_user_secret']);
$signatureV4 = new SignatureV4($options['service'], $options['region']);

$handler = new CurlHandler();

$stack = HandlerStack::create($handler);
$stack->push(
    \GuzzleHttp\Middleware::mapRequest(
        function (\Psr\Http\Message\RequestInterface $request) use ($signatureV4, $credentials) {
            return $signatureV4->signRequest($request, $credentials);
        }
    )
);

$client = new Client([
    'handler' => $stack,
    'base_uri' => $options['host'],
]);

$response = $client->post(
    '/auth/o2/token',
    [
        'Content-Type' => 'application/x-www-form-urlencoded',
        'form_params' => [
            'client_id' => $options['app_client_id'],
            'client_secret' => $options['app_client_secret'],
            'grant_type' => 'client_credentials',
            'scope' => 'sellingpartnerapi::notifications',
        ],
        'Host' => $options['host'],
        'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
    ]
);

var_dump($response->getBody()->getContents());

Hope this helps!
Let me know if you have any questions.


#4

Thank you very much for the fast reply and the hints!

Yesterday I came up with the idea using the official AWS PHP SDK for just signing my custom request, so I’m glad that we’re onto the same approach there.
Unfortunately, I’m not getting a valid response out of it.


tl;dr // summary
Currently I receive an access token via SP API JavaScript SDK. Since your PHP request doesn’t work on my side, the JavaScript is a current work-around.
With that access token I try triggering creation of an inventory report. But although my request IMHO contains all information from the docs, I get a ‘The request signature we calculated does not match the signature you provided’.

Do you have any idea what I’m doing incorrect?


1) My current use case
I need to access the sellers inventory data. Therefore IMHO I need a refresh token rather than using a grantless operation (like you do with ‘client_credentials’, right?).

Details

  • I’m sticking to the ‘Website workflow’ (see [1]).
  • Steps 1 to 3 are done via the JavaScript SDK in my frontend
    • So calling ‘amazon.Login.authorize’ gets me the MWS access code
  • Right now, I’m exchanging the access token with a long living token by JavaScript, too
    • Calling ‘amazon.Login.retrieveToken’ gets me an ‘access_token’ (lifetime 3600)
    • In that response, there’s no refresh_token.
  • Doing this exchange request on server-side failed in every case (see below for details)

2) Exchange LWA authorization code for a LWA refresh token (via server-side)
That is described in the documentation (see [2]).

Here’s an example with CURL and PHP. Both result in a HTTP 400.

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

Here’s the PHP code (adapted your code, so pasted just without the $options):

    $credentials = new Credentials($options['aws_user_key'], $options['aws_user_secret']);
    $signatureV4 = new SignatureV4($options['service'], $options['region']);

    $handler = new CurlHandler();

    $stack = HandlerStack::create($handler);
    $stack->push(
        \GuzzleHttp\Middleware::mapRequest(
            function (\Psr\Http\Message\RequestInterface $request) use ($signatureV4, $credentials) {
                return $signatureV4->signRequest($request, $credentials);
            }
        )
    );

    $client = new Client([
        'handler' => $stack,
        'base_uri' => $options['host'],
    ]);

    $response = $client->post(
        '/auth/o2/token',
        [
            'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8',
            'form_params' => [
                'client_id' => $options['app_client_id'],
                'client_secret' => $options['app_client_secret'],
                'grant_type' => 'authorization_code',
                'code' => 'ANURXXXX',
                'redirect_uri' => 'https://na.account.amazon.com/sdk/2020-10-21-npcl44qk/topic.html?uri=https%3A%2F%2FMY.URL.COM%2F&proxy=amazon-proxy-https-na_account_amazon_com&topic=sbI8ohs7S3ilxxxx&version=3'
            ],
            'Host' => $options['host'],
            'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
        ]
    );

Note: The ‘redirect_uri’ has been copied from the JS-version of the request. I also tried leaving it blank and passing just by one valid URI.

That is really odd, because with the JavaScript SDK, it’s working perfectly. Here’s the CURL version of the request the JS SDK has sent. It’s the exact same endpoint and data, just there’s a ‘code_verifier’ which I cannot find in the docs.

curl 'https://api.amazon.com/auth/O2/token' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36' \
-H 'Content-Type: application/json' \
-H 'Accept: */*' \
--data-binary '{"grant_type":"authorization_code","client_id":"amzn1.application-oa2-client.XXXX","redirect_uri":"shortened-for-readability","code_verifier":"ippAnHyxkqKC6Erms1Ob9JmFIZm3639X9dgMoOXXX","code":"ANqLLJsFXXX"}' \
--compressed

Result
So for now, I consider that a problem for solving at a later stage. At least I got an ‘access_token’ via JS. So my goal is getting inventory data with that.

3) Getting inventory data

Here I’m also using the approach of signing requests with the AWS PHP SDK signing my custom request.
According to the Github docs (see [3] Steps 2 and 3) my request contains all required headers and information (e.g. signature, date, user agent, …).

Nevertheless, I receive the error ‘The request signature we calculated does not match the signature you provided.’. With no further information, I’m really stuck on which of the many input values might be wrong.

PHP Source Code

    $credentials = new Credentials($options['aws_user_key'], $options['aws_user_secret']);
    $signatureV4 = new SignatureV4($options['service'], $options['region']);

    $request = new Request(
        'POST',
        'https://sellingpartnerapi-eu.amazon.com/reports/2020-09-04/reports',
        [
            'x-amz-access-token' => $options['seller_access_token'], // retrieved from JS SDK after seller has approved access to its accoun
            'user-agent' => 'My Tool/0.0.1',
        ],
    );

    $request = $signatureV4->signRequest($request, $credentials);
    $client = new Client();

    $requestOptions = [
        'Content-Type' => 'application/json',
        'Host' => 'https://sellingpartnerapi-eu.amazon.com',
        'json' => [
            'reportType' => 'GET_FLAT_FILE_OPEN_LISTINGS_DATA', // inventory data
            'marketplaceIds' => 'A1PA6795UKMFR9', // german marketplace
        ],
    ];

    try {
        $client->send($request, $requestOptions);
    } catch (ClientException $e) {
        $exceptionRequest = $e->getRequest();
        echo sprintf("\nURI: %s", $exceptionRequest->getUri());
        foreach ($exceptionRequest->getHeaders() as $name => $values) {
            echo sprintf("\n%s: %s", $name, implode(',', $values));
        }
        echo sprintf("\n\nMessage: %s", $e->getMessage());
    } 

Output

URI: https://sellingpartnerapi-eu.amazon.com/reports/2020-09-04/reports
Content-Type: application/json
Host: sellingpartnerapi-eu.amazon.com
x-amz-access-token: Atza|IwEBIAtelq1WRXXXX
user-agent: My Tool/0.0.1
X-Amz-Date: 20201120T142538Z
Authorization: AWS4-HMAC-SHA256 Credential=AKIA3HD5GN6XXXXX/20201120/eu-west-1/execute-api/aws4_request, SignedHeaders=host;x-amz-access-token;x-amz-date, Signature=ca61d60425bc5a6907f2f98f89bb4dXXXX
Message: The request signature we calculated does not match the signature you provided.

Disclaimer: As a new user in this forum, I cannot have hyperlinks in my message. So I split the URIs

[1] https:// github .com/amzn/selling-partner-api-docs/blob/main/guides/developer-guide/SellingPartnerApiDeveloperGuide.md#website-workflow
[2] https:// github .com/amzn/selling-partner-api-docs/blob/main/guides/developer-guide/SellingPartnerApiDeveloperGuide.md#step-4-your-application-exchanges-the-lwa-authorization-code-for-a-lwa-refresh-token
[3] https:// github .com/amzn/selling-partner-api-docs/blob/main/guides/developer-guide/SellingPartnerApiDeveloperGuide.md#step-2-construct-a-selling-partner-api-uri


#5

Hi Dialiser,
thanks for your answer! Seems like you are able to perform auth for a grantless operation, didn’t you?

By any chance, did you also do auth for using specific SP-API endpoints?

You’re using the grant_type ‘client_credentials’. For the SP-API endpoints, ’ authorization_code’ shall be used.
See https://github.com/amzn/selling-partner-api-docs/blob/main/guides/developer-guide/SellingPartnerApiDeveloperGuide.md#step-4-your-application-exchanges-the-lwa-authorization-code-for-a-lwa-refresh-token


#6

Hello, how did you get it LWA auth code, We have the same problem as you did at the beginning


#7

Hello Pure_Beauty_US,

if you need to get the auth code in context of the Selling Partner API, I would recommend the approach of having the consent URI where the seller clicks on.

As described here, you generate an URI like this:
https://sellercentral.amazon.com/apps/authorize/consent?application_id=appidexample&state=stateexample

If the seller clicks on this URI, after authorisation on Amazon-side, they will be redirected back to your page. With this redirect, I received the auth code as a query parameter.

Note: First I tired the official JavaScript SDK. Even though I received an auth code via this SDK, I was unable to trade this for a refresh token afterwards. So maybe this SDK will help you, but for me it didn’t.

Hope this helps! Don’t hesitate commenting if you need more information.


#8

Hi NetzSicht,Thank you for your reply, * So far we have access to LAW, But SP-API does not return us the refresh token! PS: * We are currently using the BATE environment 20210116141927|690x367


#9


#10

Yesterday we got the message “Refresh Token:undefined”, today we don’t even have the “Refresh Token:” field


#11

so guys,Is there a solution? I use grant_type = authorization_code
the result “error_description”: “The request has an invalid parameter : code”,“error”: “invalid_request”
I tried it many times and still got this result
If you have any suggestions, please let me know, thank you very much