The documentation for how to build a custom Shopify App using the embedded approach is not very clear. Especially if you’re using a custom backend. You can see my post on the Shopify Community Forums. I provide more details about what wasn’t working there. I have gotten it working after a lot of digging and experimentation. I wrote up what I found and how to make it work, so that you don’t have the same struggle.
Shopify App Types
You can build a Shopify in two ways: Standalone and Embedded.
A Standalone App runs and displays on its own domain. When you click on the App in the Shopify Admin, you are taken to that App’s domain. The App controls the entire browser window, and you are no longer on Shopify’s site. This can be advantageous if your App’s UX is better with more real estate, like the whole window. Standalone Apps can also be easily accessed directly by the client via a bookmark. Standalone Apps can be a typical multi-page web application. App install, and login from the Shopify Admin, are both handled via OAuth2 (mostly…. Shopify has some custom tweaks and requirements that are outside the standard OAuth2 flow).
An Embedded App is loaded into the Shopify Admin via an iFrame. It runs in a portion of the browser window, surrounded by the Shopify Admin header, footer, and left navigation block. The UI of the Embedded App has to match and align with the Shopify Admin UI. That combined with the fact that you never leave the Shopify Admin, make the experience feel more integrated and cohesive. This is why Shopify is recommending people make their Apps Embedded now. This comes at the cost of having less screen real estate to use, and more restrictive UI design options. Embedded Apps must be a single page application (SPA).
App Installation
The installation and access for the Standalone App are through (a slightly custom) OAuth2 flow. So once you have that working, it’s pretty straightforwards. Your whole app can be behind an OAuth2 authorization requirement. However, for the Embedded App it’s a whole other thing.
The main page of your Embedded App, and any resources it needs, have to NOT be OAuth protected. On that page you need some JavaScript, typically React, and load in the Shopify App Bridge libraries.
Then you need to check to see if your App has already been installed to this Store. I did this by passing the host variable, extracted from the URL, back to an API in my app. On the backend I decode it. Then strip off the protocol and “/admin” path to get just the store host name, which, at least for my implementation, will allow me to check for existing OAuth2 authorizations, i.e. App installs. If the App already HAS been installed, then the front end will proceed with the Embedded app token based authorization flow (which we will talk about later).
Building the OAuth Install URL
If the App has not been installed, then we need to initiate the OAuth installation process. We do that by redirecting the browser to Shopify’s OAuth initiation URL. We build this using the Store hostname, the App’s API key, the access scopes your App needs, and your App’s backend OAuth2 authorization URL. In this case we are getting the shopName and scopes from the response of the authCheck API call to our backend. The apiKey is configured in the app’s JS (although could also be returned from the API call).
const shopName = data.AuthCheckResponse.shopName;
const scopes = data.AuthCheckResponse.scopes;
const apiKey = config.apiKey;
const redirectUri = appApiHostname + "/oauth2/authorization/shopify";
const permissionUrl = `https://${shopName}/admin/oauth/authorize?client_id=${apiKey}&scope=${scopes}&redirect_uri=${redirectUri}`;
console.log("Redirecting to: " + permissionUrl);
// window.location.href = data.AuthCheckResponse.authRedirectURL;
// If the current window is the 'parent', change the URL by setting location.href
if (window.top == window.self) {
window.location.assign(permissionUrl);
// If the current window is the 'child', change the parent's URL with Shopify App Bridge's Redirect action
} else {
Redirect.create(app).dispatch(Redirect.Action.REMOTE, permissionUrl);
}
Once the OAuth flow finishes, the page with be reloaded into the iFrame. This time your check to see if the Store has installed the App should return true. Once we know that, we move onto retrieving the Shopify session token. The App Bridge library makes it easy with a getSessionToken method. The token expires every 60 seconds, so we have to keep grabbing the token to ensure we have a fresh one. We pass the token back to our backend APIs in the Authorization header.
On the backend the token value can be read, and parsed as a JWT token. You should validate the token by signature and expiration date. If the token is valid, you can get the Shop Name from it. That will let you load in the OAuth2 token from your OAuth data store.
Now you can use your OAuth2 token to make your API calls back to the Shopify REST and GraphQL APIs.
TLDR;
The key takeaways are:
- The App page needs to be unprotected
- You need to check if the App is already installed, before you do any App Bridge calls. Otherwise App Bridge will issue weird redirects)
- If not, trigger the OAuth install flow using the URL pattern above
- Only after it’s installed, then use App Bridge to grab the token, and don’t forget to refresh the token frequently
We’ll be publishing all the source code in our Java Spring Shopify App Framework very soon.
Hi Devon just wondering if you ever published the open source version?
We’re looking to do part embedded part external and switching to embedded auth has been a little different!
Yes, you can find it here – https://github.com/devondragon/SpringShopifyAppFramework
It needs some improvements and updates, but feel free to take a look. I’m happy to work with you to try to improve the code, functionality, documentation, etc…