May 1, 2024

13 minutes read

A developer’s guide to handling Shopify app installs

All you need to know about handling OAuth when a Shopify store installs your app.

Felipe Aragão
Founder

Developer Guide

Are you a developer building solutions for Shopify stores? In this guide I’ll show you how to handle the app installation process, which begins when a store owner clicks “Install” on your app. You will learn about Shopify apps, access tokens, OAuth, and why you should let Handshake do the heavy-lifting for you.

What you need to know

A couple of things to get out of the way:

1. Shopify has lots of different APIs

Components, functions, bridges… visiting the Shopify docs can be a daunting experience for a first-timer. There’s a lot of stuff there. Shopify gives developers a wide range of tooling to interact with e-commerce stores and personalize their purchase experience.

Shopify API reference docs – https://shopify.dev/docs/api

Shopify API reference docs – shopify.dev/docs/api

In most cases, what you want is the Admin API. This is the collection of HTTP endpoints that let you read and write to the resources in a Shopify store. For example, you can use the Admin API to read all the orders, products, and customers inside a store. And you can use them to programmatically create orders, products, and customers.

2. There’s REST and there’s GraphQL

There are two flavors of the Admin API: REST and GraphQL. A typical REST request will look like this:

# Read a page of 250 orders from the store called “example”
curl -X GET https://example.myshopify.com/admin/api/2024-01/orders.json?limit=250 \
  -H 'Content-Type: application/json' \
  -H 'X-Shopify-Access-Token: ACCESS_TOKEN'

There are a couple things worth noting here.

  • First, the host (example.myshopify.com) identifies the store you’re interacting with. Every Shopify store has a canonical “myshopify” URL, which uniquely identifies it and can never be changed. Shopify shares this URL with you during the installation process, as you’ll see below. (You can also grab it from the HTML source-code of a store. Try it out with fellowproducts.com.)
  • Second, the header X-Shopify-Access-Token is used for authentication.

Of course, the Admin API requires authentication, as you can’t go around reading sensitive data from any Shopify store. Instead, the owner of each store must grant you permission to read/write certain resources within their store. This grant results in an “access token”, a random string of characters that acts like a password when using the Shopify APIs. More on this below.

In 2018, Shopify introduced a GraphQL version of the Admin API. Requests look like this (using curl):

curl -X POST https://store-name.myshopify.com/admin/api/2024-04/graphql.json \
  -H 'Content-Type: application/json' \
  -H 'X-Shopify-Access-Token: ACCESS_TOKEN' \
  -d '{
  "query": "{
    products(first: 250) {
      edges {
        node {
          id
          title
          // ... other fields here
        }
      }
    }
  }"
}'

The GraphQL API also uses X-Shopify-Access-Token for authentication.

Shopify maintains a small set of official SDKs, which give you better ergonomics for interacting with the Admin API than making direct HTTP requests from your backend. Click here to check them out.

3. You’ll need to create an app

So how do you obtain the access token to a Shopify store? The answer is Shopify apps. Partners in the Shopify ecosystem, you can create “apps” for store owners to install. And each installation grants you an access token, which you can use to interact with a store via the API.

The Shopify App store has over 8 thousand apps – https://apps.shopify.com

The Shopify App store has over 8 thousand apps – apps.shopify.com

Public vs custom apps

Apps created by partners can have public distribution or custom distribution. Public apps can be installed by an unlimited number of stores, unlike custom apps, which can only be installed by a specific store/organization in Shopify as specified by you.

The thing about public apps is that they require Shopify approval before they can be installed by a real store. This is Shopify’s way of protecting brands from malicious actors, as well as cultivating quality in their app ecosystem.

The approval process for public apps is annoying, but nothing close to the nightmare of dealing with Google or Apple in 2024. To start, you must complete a listing and submit it for approval. Once that’s done you will typically wait several days for a Shopify representative to be assigned to you and begin evaluating your app. If they find issues with the listing or the app itself, you will work with them to fix things and ensure compliance with the App Store rules.

⭐ A frequent cause of delays in the approval process is poor implementation of the installation process, which I will cover below. Handshake helps you get approved faster by handling all the intricacies of the installation for you.

Custom apps don’t require approval from Shopify, but you will have to set them up individually for each store you want to obtain access tokens to. They are a great choice for early-stage startups that don’t mind a bit of manual work when onboarding a client.

Of course, the perk of public apps is that they get listed in the Shopify App Store. This tends to be an important top-of-funnel for developers. It’s also optional: you can hide your listing for as long as you want.

Pinterest has a public Shopify app that any store can install – https://apps.shopify.com/pinterest

Pinterest has a public Shopify app that any store can install – apps.shopify.com/pinterest

Creating an app

Creating an app is easy. First, sign up for a Shopify partner account at https://partners.shopify.com/signup. Then, from your partner dashboard, navigate to Apps > All Apps and click “Create an app”.

Create a new app by going to Apps > All Apps.

Create a new app by going to Apps > All Apps.

You can enter your app information manually, or use the Shopify CLI to developer and upload an app from your computer.

Shopify offers a CLI tool for creating apps, but the experience is still bumpy as of Feb 2024.

Shopify offers a CLI tool for creating apps, but the experience is still bumpy as of Feb 2024.

The controls of a newly-created Shopify app, showing the Client ID and secret.

The controls of a newly-created Shopify app, showing the Client ID and secret.

In the next section, you’ll see how to obtain access tokens from the stores that decide to install your app.

Store owners can also create custom apps

There’s one final way to obtain the access token to a particular store, which is for the store owner to create an app of their own and then share the access token with you. Confusingly, these are also called custom apps.

To do that, have the store owner navigate to Settings > Apps and sales channels > Develop Apps and enable app development.

Store owners can create an app bound to their stores, but that can be tricky.

Store owners can create an app bound to their stores, but that can be tricky.

Then, they’ll be able to click “Create an app”, configure the scopes you need and, finally, generate an access token. The access token will be visible inside the “API credentials” tab:

The access token becomes available straight from the interface.

The access token becomes available straight from the interface.

The benefit of store-created apps is that Shopify gives an access token directly. You don’t have to implement the app installation process as you do with a partner-created app.

The downside is friction. The average store staff won’t be able to create an app for you without help. You should be prepared to jump on a call and help them through this process yourself. You can also ask the store owner to add you as a collaborator in their store, so you can create the app yourself. They’ll need to grant you the Develop apps permission for that.

Finally, this method also makes it harder to troubleshoot and fix issues, as only the store owners will be able to change the app settings, toggle new scopes and so on. In my experience, it’s just not worth it. Create a custom or public app, and implement the app installation process yourself.

Handling Shopify app installation

Let’s see how to obtain access tokens from a Shopify store. Here’s what you’ll need:

  • A Shopify partner account (go here to create one)
  • A Shopify app, public or custom (see instructions above)
  • A development store for testing

You will need a Shopify store to test the code you’ll see below. I recommend you create a dummy store instead of using a real store. You can do this directly from your partner account by going to Stores > Add Store > Create development store.

Create a test store from your partner dashboard for testing the OAuth flow below.

Create a test store from your partner dashboard for testing the OAuth flow below.

Overview of the installation flow

Here’s the typical install flow of a Shopify app:

  1. The user (owner of a store) visits your listing in the Shopify App Store and click the “Install” button.
  2. Shopify redirects them to a page inside your website, defined by the “App URL” setting of your app. Shopify adds a ?shop= query parameter to the URL, which tells you which store is trying to install your app.
  3. Your website redirects the user to an authorization page inside Shopify, specifying which permissions (aka. scopes) your app needs from this user’s store.
  4. Shopify shows the user an installation dialog, giving them the choice to proceed.
  5. If the user chooses to install, Shopify sends them back to a callback page within your website, together with a code query parameter.
  6. Your callback page makes a direct request to the Shopify API, sending code from the URL and getting access_token in return.
  7. Your callback page shows a success page or redirects the user back to your app.

Shopify calls this process authorization code grant. It follows the OAuth 2.0 specification, which you may remember from using other APIs.

⚠️ If you have an embedded app, Shopify prefers you to use the “token exchange” method of authentication instead. Refer to their documentation to understand what is best for you.

You can start at step 3

In the flow above, steps 1 and 2 are common but optional. If a store owner is already in your website, you can start directly at step 3. You will need to ask them for their store’s “myshopify” URL, however, because you’ll need it to redirect them to the right place.

This is what you would do, for instance, if you need to obtain new API permissions from a store that has already installed your app. Or if you lose the access token to a store that had already installed your app.

Next, let’s look at the installation flow in more detail.

The flow in detail

Steps 1 and 2 of the flow are up to Shopify and the store owner. Step 3 is where your app comes in, after Shopify redirects the user to the page defined by App URL.

When users click to install your app, Shopify sends them to the App URL defined in the app settings.

When users click to install your app, Shopify sends them to the App URL defined in the app settings.

Handle redirection (step 3)

Shopify will send the user to your App URL with the following query parameters:

https://yourwebsite.com/auth/shopify/redirect?
	hmac=c10be7bf...&
	host=YWRtaW4u...&
	shop=example.myshopify.com
	&timestamp=1714410969

You will need to have a controller listening on this path. When you receive a request, you will know this is the owner of shop trying to install or log into your Shopify app.

(Technically, you must verify the request came from Shopify by using the hmac parameter. Otherwise anyone can navigate to this endpoint impersonating the owner of shop. This can become a risk, as you will see later.)

Next, you should send the user back to Shopify at:

https://example.myshopify.com/admin/oauth/authorize?
	client_id=CLIENT_ID&
	scope=SCOPE&
	redirect_uri=REDIRECT_URI&
	state=STATE

Where:

  • example.myshopify.com is the value from the shop parameter
  • REDIRECT_URI is the callback URL that you want Shopify to send users to after they agree to install. This value must match the “Allowed redirection URL(s)” setting of your app.
  • CLIENT_ID is the Client ID assigned to your Shopify app. You can find this value in your app’s dashboard.
  • SCOPE is a comma-separated list of scopes that you want to ask the store owner for. The full list is available at https://shopify.dev/docs/api/usage/access-scopes#authenticated-access-scopes

Here is some pseudo-Javascript code of what your controller will look like:

router.get('/auth/shopify/redirect', (req, res) => {
  // TODO: verify the hmac.
  const url = new URL(`https://${req.query.shop}/admin/oauth/authorize`)
	url.searchParams.add('client_id', SHOPIFY_APP_CLIENT_ID)
	url.searchParams.add('redirect_uri',
		'https://yourwebsite.com/auth/shopify/callback')
	url.searchParams.add('scope', 'read_orders,read_products')
	const nonce = crypto.randomBytes(16).toString("hex")
	url.searchParams.add('state', nonce)
	// TODO: Save the state for later.
	res.redirect(url)
})

User authorizes your app (step 4-5)

If your implementation works, the user will be redirected to a page inside their Shopify admin that looks like this:

The install app page inside the Shopify admin.

The install app page inside the Shopify admin.

This is the step in the OAuth flow when Shopify asks “User, are you sure you want to give this app this level of access to your store?”. If the user agrees, Shopify redirects them to the URL you specified via the redirect_uri parameter of step 3.

Token exchange (step 6)

Shopify will redirect the user back to redirect_uri with the following query params:

https://yourwebsite.com/auth/shopify/callback?
	code=ee61ab...&
	hmac=21b45f...&
	host=YWRtaW...&
	shop=STORE_NAME.myshopify.com&
	state=7efbfa...&
	timestamp=1714417448

code is the most important parameter here. You can exchange it for the access token by hitting the token endpoint at /admin/oauth/access_token. The call will look like this:

curl -X POST https://example.myshopify.com/admin/oauth/access_token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'code=YOUR_CODE&client_id=CLIENT_ID&client_secret=CLIENT_SECRET'

Where:

  • CLIENT_ID and CLIENT_SECRET can be found in your app’s settings.
  • example.myshopify.com comes from the shop parameter

Here is what your controller will look like:

router.get('/auth/shopify/callback', async (req, res) => {
	// TODO: verify the hmac.
	const {
		shop,
		code,
	} = req.params
	
	// Hit the token endpoint to obtain the access token.	
	const res = await fetch(
		`https://${shop}/admin/oauth/access_token`,
		{
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Content-Length": Buffer.byteLength(accessTokenQuery).toString(),
        Accept: "application/json",
      },
      body: `code=${code}&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}`,
	  }
  )
  // TODO: handle errors
	const json = await res.json()
 
	const scope = json.scope
	const accessToken = json.access_token
		
	console.log(`Success! Access token for ${shop} is ${accessToken}.`)
	res.redirect('https://yourwebsite.com/logged-in-app')
})

This is how you obtain the access token!

What’s next?

Now that you’ve obtained the access token to the user’s store, you should probably save it to your database to consult later. When you do, save also the scopes you received from the Shopify token endpoint.

type ShopifyCredential {
	shop String # example.myshopify.com
	accessToken String
	scopes String[]
}

Now it’s up to you. Once you save the access token, you should redirect the user to your app, which can be embedded within the Shopify Admin or standalone.

Appendix: A smarter redirection

Important. Shopify sends users to the App URL regardless whether they’ve already installed your app or not. You will want to modify the controller logic to check for whether a shop:

router.get('/auth/shopify/redirect', (req, res) => {
	const hasAccessTokenWithScopes = await hasAccessTokenToShop(req.params.shop)
	if (hasAccessTokenWithScopes) {
	  verifyHmacOrThrow(req.params)
	  res.redirect("/logged-in-app")
	  return
	}
	const url = `https://${params.store}/admin/oauth/authorize`
	url.searchParams.add('client_id', SHOPIFY_APP_CLIENT_ID)
	url.searchParams.add('redirect_uri',
		'https://yourwebsite.com/auth/shopify/callback')
	url.searchParams.add('scope', 'read_orders read_products')
	res.redirect(url)
})

This is where the hmac parameter from Shopify comes in. It will allow you to verify. (This part left as an exercise to the reader.)

Handshake does all of this for you

Handshake is a Next.js app that you can spin up in minutes to handle your OAuth with Shopify and over 200 other APIs. Set up is super easy, and involves the options.ts file.

import {
  HandshakeOptions,
  Shopify,
  ShopifyCredential,
} from "handshake";
 
export const options: HandshakeOptions = {
  allowedRedirectHosts: ["https://yourwebsite.com"],
  handlers: [
    Shopify({
      clientId: process.env.SHOPIFY_CLIENT_ID!,
      clientSecret: process.env.SHOPIFY_CLIENT_SECRET!,
      scopes: [
        "read_orders",
        // ...
      ],
    }),
	],
	async onSuccess(credentials, handlerId) {
		// Send the newly-obtained access token to your backend via parameters
		// or API.
	}
}

As always you can reach us at team@fiber.dev with any questions.