Setting up OAuth2 authentication and getting tokens with the Shopify API

Like many other third-party app APIs, Shopify provides a way to authenticate a user account with your app, and then use the returned token to make API requests for the user’s data.

I implemented an OAuth strategy for the Shopify API a while ago. I re-visited this code recently because I was defining a strategy for a different API using Passport JS. Passport abstracts the OAuth process away, so it is easy to use but hard to figure out what is actually going on. Looking back at my Shopify API code, the OAuth2 strategy process was more transparent.

I used the npm shopify-node-api module and built my own shopify-api wrapper module, for promisifying and other stuff. This post just covers the initial two-part OAuth2 process for getting an access_token from Shopify.

So here’s what happens:

I have already authenticated my app user separately (e.g. just using local auth). My client now makes a RESTful request to my own server api to authenticate a connection to a user’s Shopify shop. This RESTful request looks something like this:

https://myApp.com/api/platform/auth/user12345/shopify/MyUsersShop

The above request routes to an Express auth middleware. (passing in three request params - a userId, ‘shopify’ to denote I want to use the Shopify platform, and the name of the user’s Shopify shop ‘My UsersShop’), the server needs to use these params to contact Shopify.

As an aside - I need to ultimately re-direct this request to Shopify (the third party), and I ran into issues with re-directing an XHR from the client. I solved this in a hacky way by setting the window.location to the above URL (which is a window refresh rather than an XHR).

At this point I use the shopify-node-api module to generate an authentication request URL to Shopify. This involves generating a config object which contains my app’s registered Shopify app ID and secret, the user’s shop name, the scope of permissions I require for the user’s shop on Shopify, and importantly, a re-direct URL. This re-direct URL is called back by Shopify back to my application and importantly will have a query param attached which contains the one-time-use token returned by Shopify as the first part of the auth process.

This re-direct URL (called back by Shopify) looks something like this:

https://myApp.com/api/platform/auth_success/user12345/shopify/MyUsersShop

So the app Id, app secret, user’s shop name, permissions scope, redirect URI (which has the user’s ID for my app) all get sent as a config to a factory function in the shopify-node-api module, which looks like this:

ShopifyAPI.prototype.buildAuthURL = function(){
    var auth_url = 'https://' + this.config.shop.split(".")[0];
    auth_url += ".myshopify.com/admin/oauth/authorize?";
    auth_url += "client_id=" + this.config.shopify_api_key;
    auth_url += "&scope=" + this.config.shopify_scope;
    auth_url += "&redirect_uri=" + this.config.redirect_uri;
    return auth_url;
};

This returns a nice big authentication URL for calling Shopify which my app re-directs to from my server (having received the initial request from my client, see ‘aside note’ above).

So now off we go to Shopify in the browser and it either asks for user authentication and acceptance of my app, or if my app is already registered with the user’s Shopify shop then it calls back the re-direct URL straightaway.

This re-direct URL routes to an Express middleware function on my server. This middleware now extracts the query param from the request made by Shopify and passes it back into another function in the shopify-node-api module called exchange_temporary_token. This function extracts the code property from the query string (returned by Shopify as the first part of the OAuth process). It posts this code as part of a data payload back to Shopify again to get returned the actual access_token which is the second part of the OAuth process.

ShopifyAPI.prototype.exchange_temporary_token = function(query_params, callback) {
    var data = {
            client_id: this.config.shopify_api_key,
            client_secret: this.config.shopify_shared_secret,
            code: query_params['code']
        },
        self = this;

    if (!self.is_valid_signature(query_params)) {
        return callback(new Error("Signature is not authentic!"));
    }

    this.makeRequest('/admin/oauth/access_token', 'POST', data, function(err, body){

        if (err) return callback(new Error(err));

        self.set_access_token(body['access_token']);
        callback(null, body);

    });
};

I can now store this access_token in the database (or key/value store) and use it to make subsequent requests for user data from the Shopify API.

Written on September 9, 2015