DEV Community

Daniele Cruciani
Daniele Cruciani

Posted on

Implementing Authorization Workflow (on Amazon SP API)

Amazon Selling Partner API documents two kinds of authorization workflow, one starting from your website app https://developer-docs.amazon.com/sp-api/docs/website-authorization-workflow, and the other one starting from the Amazon App store.

Authorize from your website

While implementing the authorize workflow from your website, you start assuming that the user is already logged in. This means the browser is keeping a set of cookies associated with your domain, one of those being the session_id (however your framework is naming it does not really matter).

So, the main assumption is that, wherever the user goes after visiting your page, when he or she comes back to your website the session cookie allow your website to identify the user_id (or however your system names the user identifier).

Now, following direction and steps described in https://developer-docs.amazon.com/sp-api/docs/website-authorization-workflow, whenever the workflow lay on your website, in addition to state and auth_code, the session_id is another implicit information you have to match the request.

So, why on earth you need to send a state field during the step 2? Well, your system does not know how many browser are connected to the same account, maybe from the same real user, but just not active.

That means your application must take track and awareness about the authorization process, and from which session_id it where started.

REST is stateless

Most of the application are implemented using a REST API and a frontend framework, in my example there is gateway verifying a JWT, a backend microservice handling the request, and a ReactJs frontend making REST calls.

Semplified Sequence diagram
Semplified Sequence diagram
This diagram is semplified, microservice run in a Cloud environment, and has access to database, redis, and whatever service is required.

When user start an Amazon authorization process the React App call a specific API, how does is it associatated with the specific instance of the React App?

This is done by:

  1. generating an UUID (see https://www.npmjs.com/package/uuid)
  2. storing that UUID as a cookie with lifetime of 10 minutes
  3. using a POST /auth-gen request with that uuid (I call it cookie_uuid). The API reply with an authorization url, as described in step 2.

In the backend:

  1. POST /auth-gen call: the state is generated by uuid() and stored in a redis server with the key userid-[state] and value of received cookie_uuid
  2. POST /auth-verify call: with the payload of state, selling_partner_id, mws_auth_token (it is void, afaik), spapi_oauth_code, cookie_uuid, it verify the key userid-[state] exists in the redis and it contains the sent value cookie_uuid, then proceed with LWA staff acquiring the refresh_token, and storing it somewhere
  3. POST /auth-assign-account call: this call assign the specified acquired (and pending) refresh_token, to internal structure associated with the customer. This is just in case your system support more external selling platforms REST is stateless, but redis implement what is needed, also the browser cookie is keeping track of the process

Looking at the sequenceDiagram above, on the two extreme, both Microservice and ReactApp have access to some support to keep the state persistent, even if the service or the ReactApp is switched off: a stateless microservice has redis and db, ReactApp has browser cookie and/or browser localstorage

The React Application: how to recognize the time to verify app
The only way for the frontend app to recognize the page was opened after a redirect from amazon (with granted authorization) is by parsing the get parameters. Assuming we are using React+Redux, and also hook and effect, somewhere in the main App there should be something like:

const params = getQueryParams(location);
const dispatch = useDispatch();
  useEffect(() => {
    if (
      params.get("state") &&
      params.get("selling_partner_id") &&
      params.get("spapi_oauth_code")
    ) {
      let payload = {
        state: params.get("state"),
        selling_partner_id: params.get("selling_partner_id"),
        spapi_oauth_code: params.get("spapi_oauth_code"),
        mws_auth_token: params.get("mws_auth_token"),
      };
      dispatch({
        type: AuthorizeAmazon.AUTHORIZE_CONFIRM_FROM_AMAZON,
        payload,
      });
    } else {
      console.log("NOT DISPATCH");
    }
Enter fullscreen mode Exit fullscreen mode

Where const getQueryParams(location) is something like:

const getQueryParams = (s?: string): Map<string, string> => {
  if (!s || typeof s !== "string" || s.length < 2) {
    return new Map();
  }

  const a: [string, string][] = s
    .substr(1) // remove `?`
    .split("&") // split by `&`
    .map((x) => {
      const a = x.split("=");
      return [a[0], a[1]];
    }); // split by `=`
  return new Map(a);
};
Enter fullscreen mode Exit fullscreen mode

The in redux action for AuthorizeAmazon.AUTHORIZE_CONFIRM_FROM_AMAZON there is code for retrieving cookie data and POST /auth-verify

Frontend / Backend decoupling

With this implementation the backend maintain the knowledge of the application_id, and also for the generation of authorization url, whose are returned by the API call and used by React App to redirect to amazon, or whatever.

Still, there is logic that are well bound between frontend and backend: the cookie_uuid.

In fact cookie_uuid and redis storage implement a kind of state bound between user browser and backend

This is ok since there is a expiration time associated with that, on both side

From App Store Authorization Workflow

I had no direct experience on implementing the authorization workflow from the the App Store, it would be more complex if the customer has no account on our website, in this case in fact a quick registration process must be implemented: the process can take no more than 10 minutes. This constraint can be really critical, since amazon is not providing any customer information via its API, neither their email, just the merchant_id. So your system must be elastic enough to accept different kind of identifiers. Another option is to assign a temporary email address, then give the customer the option to change this temporary email address with its real email address. You can also force your customer to specify and verify a real email address, in order to try/activate your service.

The workflow is described in https://developer-docs.amazon.com/sp-api/docs/selling-partner-appstore-authorization-workflow

Top comments (0)