Authenticate A Shopify App Using OAuth — The Ballerina Way

Thisaru Guruge
Ballerina Swan Lake Tech Blog
8 min readJun 2, 2020

During this pandemic, we now feel the importance of E-commerce more than ever. Shopify is an E commerce platform which provides merchants to create an online store easily.

We, as developers can create various apps on Shopify to help merchants to handle their stores. In order to install a Shopify app, merchants needs to authenticate the app, and the app needs an access token to access to the stores.

I was trying to create a Shopify app (for a completely different reason). Although there are many documentations, I spent many hours to find a way to authenticate my app. (Sounds dumb, yeah!).

Finally, I succeeded, and wanted to share the way I accomplished this using Ballerina. Ballerina is specifically designed for network applications. It is pretty much simple to do this, even compared to the solution provided by the Shopify themselves! You only need Ballerina for this.

If you haven’t downloaded Ballerina yet, do it now.

First, let me briefly explain the authentication process. This is explained in detail at shopify documentation. There are 5 steps (These steps are defined by me, don’t take it as an official statement).

  1. Merchant sends a request to install the app. (This sends a request to the App installation endpoint).
  2. App installation endpoint will then redirects the request to the shopify admin API, with the required scopes, and a redirect_link. (This redirect_link is the link to the endpoint described in the step 4 below).
  3. Then the Shopify will show a prompt to the user to review the scopes and authorize the installation request.
  4. Then the Shopify admin API sends a request to the app. (This request is sent to the redirect_link described in the step 2).
  5. The app endpoint then validate the request from the Shopify (Will describe this later), and sends a request back to Shopify admin API to get an access token.

First thing is to create an App and get the API key and the API secret key for the App. Creating an app is really easy, and you can follow the instructions provided by Shopify documentation for this. I’m not going to explain this further since that isn’t my intention here.

But there’s one important thing. You have to provide the aforementioned endpoint URLs in the Shopify app settings. Use the following settings in your URL section in the app settings.

For this example, I used localhost. You may replace these URLs with your host, but for testing, using localhost is much easier.

Following are the URLs

  • App URL
    When a merchant tries to install your app, this is the place where it redirects the merchant. In this example, we are using install endpoint for this.
  • Whitelisted Redirection URL(s)
    These are the redirect URLs used for authentication purposes. For this example, we only use the get_token endpoint for this. Make sure to use the exact endpoint url here, providing just the domain will not work.

First get your API key and API secret key from the Shopify app page. We need this to authenticate the app. It looks like following:

Now we are going to write the install endpoint using Ballerina. This endpoint have to do the following:

  • Read the query parameters from the request and retrieve the store name.
  • Send a request to Shopify admin API, with client_id, scope, and the redirect_uri as query parameters.

Following is the ballerina service for this. This is just for the explanation. The complete code can be found at the end of this article.

@http:ServiceConfig {
basePath: "/"
}
service ShopifyAuthenticationService on new http:Listener(8080) {
resource function install(http:Caller caller, http:Request request) {
map<string[]> requestParameters = request.getQueryParams();
string[] shops = <string[]>requestParameters["shop"];
string redirectUrl = <string>encoding:encodeUriComponent(getTokenResource, "UTF-8");
string shop = shops[0];
string queryParameters = "?client_id=" + API_KEY + "&scope=" + scopes + "&redirect_uri=" + redirectUrl + "&state=" + NONCE + "&grant_options[]=" + accessType;
string shopUrl = "https://" + shop + authorizeApiPath + queryParameters;
http:Response response = new;
var result = caller->redirect(<@untainted>response, 301, [<@untainted>shopUrl]);
}

First we need to retrieve the store name from the query parameters. Ballerina HTTP module provides APIs for retrieve query parameters from the request path easily.

Then using the shop name, we build the shop URL to redirect the request to authenticate the app. AS mentioned earlier, we need to add the following query parameters to the request path.

  • API key (client_id)
    The API key of the app, which we received from the shopify.
  • Scopes (scope)
    These are the access scopes, which are required for our app. There are a lot of app scopes available, and you can decide what scopes needed for the app depending on the requirement. These should be provided as a comma separated string.
  • Redirect URI (redirecrt_uri)
    This is the app endpoint link, which is used to request the access token from the shopify, as described previously. It should be encoded before sending. Ballerina provides an easy way to encode a URL using Ballerina encoding module.
  • State
    This is a random nonce string. It is used to validate the request when it comes back from the Shopify admin API. For the example, we just use a predefined string.
  • Grant Options
    This is the type of access token we are requesting. There are two types. The offline access is used here, which provides kinda permanent access token. Read more about API access modes here.

Then we create a new, empty http:Response object to send to shopify admin API. This response is needed to redirect the request to the Shopify admin API. It has nothing to do with anything with the authentication process.

Then the request is redirected to the previously built shop URL. Note the status code 301 (http:REDIRECT_MOVED_PREMANENTLY_301) is used when redirecting. This must be 301, otherwise the redirect will fail.

The @untainted annotation is used to untaint the tainted values in Ballerina. Read more about Ballerina taint checking.

Then shopify will redirect this back with a code. We use this code to retrieve an access token. To get this access token we have to implement the get_token endpoint. Following is the code. (This resource function also resides inside the service we created before).

resource function get_token(http:Caller caller, http:Request request) {
map<string[]> requestParameters = request.getQueryParams();
string path = removeHmacParameter(requestParameters);
string[] shops = <string[]>requestParameters["shop"];
string shop = shops[0];
string[] hmacs = <string[]>requestParameters["hmac"];
string hmac = hmacs[0];
string[] states = <string[]>requestParameters["state"];=
string state = states[0];
string[] codes = <string[]>requestParameters["code"];
string code = codes[0];
string redirectUrl;
http:Response response = new;
boolean isValid = validateRequest(path, hmac, state);
if (isValid) {
log:printInfo("Valid request");
json requestBody = {
client_id: API_KEY,
client_secret: API_SECRET,
code: code
};
request.setJsonPayload(<@untainted>requestBody);
string accessTokenUrl = "https://" + shop + accessTokenApiPath;
http:Client httpClient = new (accessTokenUrl);
var result = httpClient->post("/", <@untainted>request);
if (result is http:Response) {
json responsePayload = <json> result.getJsonPayload();
string accessToken = responsePayload.access_token.toString();
log:printInfo("Access Token: " + accessToken);
}
redirectUrl = "https://your.app.com/success";
} else {
log:printError("Request is invalid");
redirectUrl = "https://your.app.com/failure";
}
var redirectResult = caller->redirect(response, http:REDIRECT_MOVED_PERMANENTLY_301, [redirectUrl]);
}

The request contains the following query paramters:

  • code
    Used to retrieve the access token from the Shopify.
  • shop
    The name of the shop.
  • hmac
    Used to verify the authenticity of the request.
  • state
    The nonce value sent from the previous step.
  • timestamp
    This is used to calculate the HMAC value.

Now the request should be validated using the HMAC value. To do this, we need to remove the hmac parameter from the query parameters, and then calculate the HMAC value. Then comparing the generated HMAC value and the received HMAC value, the authenticity of the request can be validated. The Ballerina Crypto module can be used for this. Following functions does this:

function validateRequest(string path, string hmac, string state) returns boolean {
if (NONCE != state) {
return false;
}
byte[] pathValue = path.toBytes();
byte[] hashKey = API_SECRET.toBytes();
byte[] resultHmacBytes = crypto:hmacSha256(pathValue, hashKey);
string resultHmac = resultHmacBytes.toBase16();
return resultHmac == hmac;
}
function removeHmacParameter(map<string[]> requestParameters) returns string {
string result = "";
string[] keys = requestParameters.keys();
foreach string key in keys {
if (key != "hmac") {
string[] value = <string[]> requestParameters[key];
result = result + "&" + key + "=" + value[0];
}
}
// Remove the first `&` character
return 'string:substring(result, 1);
}

The removeHmacParameter function will remove the hmac parameter from the query parameters and returns a string. Then the validateRequest function will compare the nonce value and the hmac value, and return a boolean value to indicate whether the request is a valid request from the Shopify or a malicious one.

Once the request is validated, we can proceed to retrieve the access token from the Shopify admin API.

Then we create a Ballerina http:Request object to send to the Shopify admin API to retrieve the access token. This request contains a JSON body. We create this JSON object using Ballerina. Since json is a built-in type in Ballerina, this is quite simple. The JSON should have the API key, the API secret key (which are obtained in our app page), and the code sent by Shopify. After creating the JSON object, it is set as the request body, and then a post call is sent to the authenticate endpoint of the Shopify admin API.

The Shopify API will then send a response with the access token. This token is sent inside the response body, as a JSON. The response payload is read and the access token is retrieved as shown in the code.

We only print the access token here, but you can save this access token to communicate with the store in future.

Finally, we redirect the request from the Shopify admin API back to the shop URL. This URL can be anything. You can use your app home page or anywhere else, so that after installing the app, merchant is redirected to that URL.

That’s it! We now have an access token to communicate with a shopify store, who installed our Shopify app.

Here’s the complete code for the example:

This is it. This is all you want to authorize a Shopify app. Hope this helps you out. If you have any questions, catch me on Twitter.

Note: Ballerina and Shopify are registered trademarks of the respective companies.

--

--