Amazon SP-API Auth & Auth Demystified

Marco Tibaldeschi
9 min readApr 29, 2021

Hi everybody, this article is to set into the stone my discoveries about Amazon Selling Partner APIs (SP-API) Authentication that, in my opinion, is a little bit difficult to implement, even because the official documentation misses to explain some crucial parts.

First of all, this article is for all of those who, like me, need to migrate their Amazon MWS (Marketplace Web Services) application to the new SP-API but fits perfectly also to those who would like to create a brand new application communicating with SP-API.

Step 0. Setup

In order to fully follow me in this process you will need:

  1. a Seller Central account, where you created your Amazon App.

2. a Registered Application onto Seller Central. By clicking on “View” under the “LWA Credentials” into “Developer Central” (Seller Central > Apps & Services > Develop Apps), you will get:

  • a Login With Amazon Client Id (Client Identifier)
  • a Login With Amazon Client Secret (Client Secret)
  • at least one OAuth Redirect URI (defined in the “App Registration” section, reachable by clicking the “Edit” button)
  • the OAuth Login URI (defined as before). This is not needed for the flows, but if you don’t set it, you won’t be able to test the autorization code flow.

3. an AWS account where, if you followed the official SP-API documentation, you should:

  • have created a User (IAM > Users) for which you should have his Access key ID and Secret access key
  • have created a Role (IAM > Roles) that should have an attached Policy that allows members to “Invoke” the “Execute API” service. For this Role you should have its ARN
  • assigned to the User an inline policy that allows him to perform a STS Access Role for your defined role ARM

So, in order to sum up, these are the settings you need in order to go on:

  • Login with Amazon Client Id (aka LWA Client Id)
  • Login with Amazon Client Secret (aka LWA Client Id)
  • AWS User Access Key ID (aka AWS UserId)
  • AWS User Secret Access Key (aka AWS SecretKey)
  • AWS Role ARN (aka Role ARN)

Keep all of them near you, because this will be a long trip. Bring some water or coffee as well.

Step 1. Authoriziation

Authorization is performed by providing SP-API endpoint with a valid Access Token obtained using Login With Amazon API.

An Access token may be obtained in three ways. The basic request is like the following, with some extensions for each different request.

POST https://api.amazon.com/auth/o2/tokenHeaders
Content-Type: application/x-www-form-urlencoded
Body
grant_type: [depending on the flow, see next]
client_id: [your Login With Amazon Client Id]
client_secret: [your Login With Amazon Client Secret]
  • Authorization Code Grant, on behalf of a Seller user, by using one of the two flows: Marketplace Appstore workflow or Website workflow. When you complete the flow, you get both an Access Token and a Refresh Token (see next).
    In this flow the “grant_type” parameter equals to “authorization_code”.
    You must also pass these additional parameters:
code: [received by Amazon as querystring parameter]
redirect_uri: [your application OAuth Redirect URI]
  • Refresh Token Grant. If you have a Refresh Token, you can get a new couple of Access Token and Refresh token. In this flow the “grant_type” parameter equals to “refresh_token”. You must also pass these additional paramters:
refresh_token: [the Refresh Token you want to use]
  • Client Credentials Grant. This may be needed in order to have an Access Token that is not bound to a particular Seller User. This kind of token is needed for the so-called “Grantless Operations”, one of which is the getAuthorizationCode that is needed for all of those who currently have MWS tokens and want to convert them to SP-API tokens.
    In this flow the “grant_type” parameter must be set to “client_credentials”.
    This flow doesn’t provide you with a Refresh Token, so you must perform this call each time you need a token.
    You must also pass these additional parameters:
scope: sellingpartnerapi::notifications sellingpartnerapi::migration

Available scopes may be found here, and you don’t need to pass both of them. Just pass what you need for your calls.

After you completed the Authorization step, you should have in your hands a valid “Access Token” whether linked to a particular Amazon Seller (authorization code grant or refresh token grant) or not (client credentials grant). Time to celebrate!

Step 2. Authentication

Normally, for other kinds of APIs an Access Token, is enough to invoke them. This opens to some security issues, since if a third intercepts the Access Token, he will be able to perform calls faking it’s you. In order to solve this problem, Amazon opted for an additional layer of security, requiring developers to sign their requests using their AWS signature called “Signature Version 4”.

Here we are with the difficult part, at least for me, since it took me few hours (read it days) to understand all the flow, even because documentation lacks a lot of parts.

First of all, after this process, three HTTP headers will be added to your SP-API request:

  • x-amz-security-token, that will contain the AWS STS Session Token
  • X-Amz-Date, that will contain the timestamp of the previous signature
  • Authorization, that will contain the Signature Version 4 of your request

These three HTTP headers (aka “Mystic Headers”) are to be added, for each request, to the standard two that are:

  • User-Agent, containing your User-Agent description (i.e. your application name). This is an easy one.
  • x-amz-access-token, that contains your Login With Amazon Access Token (the one that you obtained in the previous step).

Please note that the x-amz-access-token is completely different than the x-amz-security-token. Don’t confuse them, like I did. Please. I ask you to do so. It will save to you a lot of time. And don’t believe to those who says that you won’t need x-amz-security-token, because I never ever succeeded in talking to Amazon SP-API without it.

Let’s discover now how to generate the three mystic headers.

2. 1 — Generating the x-amz-security-token Header

First of all you need to generate this header, because you will need it even for the next step. In order to generate it you will need to:

  • make a call to the AWS STS “AssumeRole” API. I used the AWSSDK.SecurityToken .NET package, but I guess you can just make a raw call. The “AssumeRole” call will let your AWS programmatic user, assume the role that, if you remember, has the authorization to call the SP-API.
    So, in order to make this call, you will have to have your AWS user’s credentials and the role ARN you want your user to assume.

Here is a sample code:

AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest()
{
RoleArn = [AWS Role ARN],
RoleSessionName = Guid.NewGuid().ToString()
};
AmazonSecurityTokenServiceClient client = new AmazonSecurityTokenServiceClient([your AWS User Access Key Id], [your AWS User Access Secret Key], [AWS Region]);
AssumeRoleResponse resp = client.AssumeRole(assumeRoleRequest);

Please note that each SP-API endpoint refers to different AWS regions, so you will need to make the call to the right one.

  • if the call succeeds, you will have a response that will contain a “Credentials” element. This is critical because it contains:
  • A “SessionToken” that is the value you must specify for the x-amz-security-token Header header (yahooo!!!)
  • A temporary couple of new AWS User Access Key Id and AWS User Access Secret Key that are linked to your user which has been granted your role. It’s very important that you save this credentials because they are needed for the next step!

2.2 — Generating the X-Amz-Date header

Generating this header is trivial. Just create a timestamp of the current date in this format: yyyyMMddTHHmmSS (e.g. 20210429T143830Z).

Easy one!

2.3 — Generating the final monster: Authorization header

In order to generate the Authorization header, you must follow the process described into AWS documentation. This task is composed of 3 steps:

  1. Create a “Canonical Request” string
  2. Create a “String to sign”, using the “Canonical Request”
  3. Sign the “String to sign”: this is the signature

The “Canonical Request” has to be built using this process. It is basically a string containing the following elements, each separated by a \n character:

  • Method of your request (e.g. GET)
  • The resource you are calling (i.e. the API endpoint, e.g. /authorization/v1/authorizationCode)
  • A list of all the querystring parameters you are passing, all of them canocalized and sorted in alphabetical order
  • A list of all the HTTP headers that are sent with the request with their corresponding values. Note: you must include both the X-Amz-Date and the x-amz-security-token. If everything is correct you should include: host, user-agent, x-amz-access-token, x-amz-date and x-amz-security-token. Obviously Authorization header will not be included, since you are generating it.
  • A list of all the included HTTP headers, separated by commas
  • Finally an HEX-encoded SHA-256 hash of your request body. If the request doesn’t have a body, compute it from an empty string and you will obtain the “e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855” value

After you built your “Canonical Request”, you must generate the “String to Sign” (that is composed by the “Canonical Request” and other elements). Follow this guide for precise details in order to generate your “String to sign”.

For clarity, this “String to Sign” is composed by the following components, each separated by a \n character:

  • the Algorithm, fixed to “AWS4-HMAC-SHA256”
  • the Datetimestamp (same as X-Amz-Date header)
  • the “Credential scope” string which is composed like by the date timestamp, the AWS region, the AWS service and “aws4_request” terminator. The AWS service we need to use for SP-API is “execute-api”. So a sample “Credential scope” string is “20210429/eu-west-1/execute-api/aws4_request”. Note that the date part doesn’t include time.
  • the HEX-encoded SHA-256 has of the “Canonical Request”

Just to give you an example, here is an example of a “String to Sign”:

AWS4-HMAC-SHA256
20210429T143830Z
20210429/eu-west-1/execute-api/aws4_request
99c0dc10a393a8717ce98c7384c2eeb862bdf47e45bd7ed61ffe4aa162c6f326

We are almost there. We just need to sign this string using our temporary AWS credentials obtained before. This is very important: you don’t have to use your original AWS Credentials, but the one you received from AWS STS after you called the “AssumeRole” API. If you miss this, you will sign the string using not-authorized keys and you will get back an error. And maybe you will enter into the loop, believing that the problem is into the “Access token”.

In order to sign the string follow this detailed explanaition.

Finally, after you generated the signature the Authorization header value must be composed like this:

[Algorithm] Credential=[Credentials], SignedHeaders=[Headers], Signature=[Signature]

Where:

  • Algorithm is set to “AWS4-HMAC-SHA256
  • Credentials is a concatenation of you temporary AWS User Access Key and the “Credential scope” (see before)
  • SignedHeaders is the concatenation of the headers you used to generate the “Canonical Request” (they should be host;user-agent;x-amz-access-token;x-amz-date;x-amz-security-token)
  • Signature is the signature you composed just few minutes ago, using your temporay AWS user credentials

This is a sample Authorization header value

AWS4-HMAC-SHA256 Credential=ASIA5XKLGQBXXKY6TG76/20210429/eu-west-1/execute-api/aws4_request, SignedHeaders=host;user-agent;x-amz-access-token;x-amz-date;x-amz-security-token, Signature=cfb940327fa969919b850a11d5c570f0e1ee1d9a2b637db06ae1ed506d655477

Summing up

As far as I know, in order to make a SP-API call, you will set those HTTP headers:

  • User-Agent
  • x-amz-access-token
  • x-amz-security-token
  • X-Amz-Date
  • Authorization

All of them are needed in order to recive a correct and succesful response (if all of them are correct, of course!)

Conclusions

I guess that Amazon put a lot of effort into securing their new SP-APIs. This kind of Auth&Auth protocol is very complex, but I guess it’s very robust. A malicious user that wants to call a SP-API won’t need only your seller’s tokens, but he will also need your AWS user credentials.

If you keep them into a secure storage, like Azure Key Vault or AWS Secrets Manager, you will have built a safe mechanism to provide a good service to your customers and good and safe nights to you and your collegues.

Pat yourself on the back, you did it!

Update 2021–05–05

Beware! Amazon SP-API checks headers in a case sensitive way. This means that at least those two headers must be written and sent like this:

  • x-amz-access-token
  • x-amz-security-token

I’ll file a bug on GitHub because I really guess that check shouldn’t work like that, but right now, if you send them in different ways (e.g. X-Amz-Access-Token), they are totally ignored and will give you back errors on tokens!

Update 2021–05–18

Beware! Some sets of APIs are provided in different versions. For example, Feeds API are documented with a 2020–09–04 and 2021–06–30 versions. If you use the latter, you may get Authentication error, even if the signature headers are perfectly valid.

I currently don’t know how to understand which version is live, stable and safe to use.

Update 2021–06–15

Regarding the previous note, I found — discussing with Amazon support — that published versions (at least published on GitHub) are usable as soon as the date in the version name is reached. This means that the version “2021–06–30” cannot be used until that very day and, if you try to use it before, you will get an authentication error. Thanks also to Lee Field who wrote to us and let me remember to write this update here.

--

--