Authenticate an embedded app using session tokens

This tutorial describes the frontend and backend changes required to set up session token authentication in your embedded app.

Requirements

Make frontend changes

You need to make the following changes to the frontend to set up session token authentication in your embedded app.

Load Shopify App Bridge in a skeleton page

After your unauthenticated routes serves the initial JavaScript, create an App Bridge instance on the frontend.

For more details on setting up Shopify App Bridge, refer to Getting started with Shopify App Bridge.

Start fetching protected data with session tokens

After you create the App Bridge instance, you can start getting session tokens and passing them in requests to fetch protected merchant data.

Each time you make a request to fetch protected resources from the app backend, you need to include the session token as an Authorization header in the following format: Authorization: Bearer <session_token>.

App Bridge provides utilities for getting session tokens and passing them into requests made to the app backend. For more information, refer to Get session tokens using App Bridge utilities.

Make backend changes

You need to make the following changes to the backend to set up session token authentication in your embedded app.

Decode session tokens for incoming requests

The Shopify App gem and Shopify Node API library provide middleware and utilities for decoding session tokens.

If your app isn't built using the libraries mentioned above, then you can obtain session details from a session token manually.

Allow authenticated requests

To allow authenticated requests, you need to update the route that serves the app so that it allows unauthenticated requests You also need to add logic to the unauthenticated route to detect if this is the first time the shop is loading your app.

1. Update the route

If the page that is server-rendered by this route depends on an authenticated request to the route, remove the protected data from the response and expose it to the frontend using an authenticated API route.

For example, your app might be embedding merchant information in its initial HTML response.

The only merchant information available on unauthenticated routes is the shop domain passed in as a query parameter in the app URL.

2. Add logic to the unauthenticated route

If your app doesn't have a shop/offline access token for the shop, then the route should respond with a redirect to send the user to the OAuth login flow. This ensures that the app is installed and receives its shop token.

If your app uses user/online access tokens, then you will need to redirect the user a second time to the OAuth flow.

Add middleware

You need to add middleware that detects requests with a session token present and builds a session based on the shop and user information included in the token.

To verify the authenticity of a session token, refer to Verify the signature.

Mark shop records as uninstalled using the app/uninstalled webhook

To ensure OAuth continues to work with session tokens, your app needs to make sure shop records are updated when a shop uninstalls your app. Your app can receive notifications of uninstalls by subscribing to the app/uninstalled webhook.

You should also set up the webhook subscription for any shops that have already installed your app. For example, you could use the webhookSubscriptionCreate endpoint, or define a background job to subscribe to the webhook.

Handle changes to access scopes requested by your app

Your app needs to keep track of the access scopes currently authorized for each shop to determine whether they match the scopes currently requested by your app.

If the scopes currently authorized don't match the scopes currently requested by your app, then your app needs to initiate the OAuth login flow to ask the merchant to authorize the new scopes:

To determine whether currently authorized scopes match currently requested scopes, you can store the current access scopes on your app or query them using the GraphQL Admin API or REST Admin API:

  • GraphQL Admin API: Query the accessScopes field on the AppInstallation object.

  • REST Admin API: Make a request to the GET /admin/oauth/access_scopes.json endpoint on the AccessScope resource.

It's important to consider app load performance when making network calls to request access scopes using APIs.

Handle the expiry of online access tokens

Apps that use online access tokens need to keep track of whether the token is expired.

If the online access token is expired, then the user needs to go through the OAuth login flow to retrieve a new token.

Obtain session details manually

If your app isn't built using the Shopify App gem, the Shopify Node API, or another library with session tokens included, then use the following steps to help you manually decode the session token and verify that it's valid. The session token is signed using the shared secret of your Shopify app.

Steps

  1. Decode the session token, verify the signature, and obtain the headers and payload.
  2. Extract the exp value from the payload.

    Verify that it's in the future.

  3. Extract the nbf value from the payload.

    Verify that it was in the past.

  4. Extract the iss and dest fields from the payload.

    The top-level domains should match. The dest field specifies the shops that the request originated from. For example, myshop.myshopify.com.

  5. Extract the aud value from the payload.

    Verify that it matches the API key of your app.

  6. Extract the sub value from the payload.

    This is the ID of the user that made the request.

If any of the above steps fail, then discard the payload, stop processing the request, and respond with an error.

Verify the signature

To verify that the signature is correct, you need to generate a new Base64url-encoded signature using the app’s shared secret.

Session tokens are signed using the HS256 algorithm. This is a symmetric algorithm. The signing key is the shared secret for your Shopify app. A session token is a JWT string with the following structure: <header>.<payload>.<signature>

All three sections are base64 encoded.

Use the following steps to verify that the issued token has a valid signature. There is a useful token analysis tool at JWT.io.

Steps

  1. Take the <header>.<payload> portion of the string and hash it with SHA-256.
  2. Sign the string using the HS256 algorithm by using the app’s secret as the signing key.
  3. Base64url-encode the result.
  4. Verify that the result is the same as the signature that was sent with the session token.

Make sure shop records are updated

To ensure OAuth continues to work with session tokens, your app must update its shop records in the event a shop uninstalls your app. The app can receive notifications of these events by subscribing to the app/uninstalled webhook.

The following sections show a Ruby implementation that subscribes to the webhook and updates the records.

Set up the webhook

Use the add_webhook generator from the shopify_app gem to set up the app/uninstalled webhook for the app. The following code adds the app/uninstalled webhook to your app config and creates the AppUninstalledJob job class so that you can add uninstallation handler logic.

Mark the shop record as uninstalled

To mark the record as uninstalled, you need to update the AppUninstalledJob job class. In the following example, the app marks the shop as uninstalled by deleting the Shop record:

Define a background job

You need to define a background job to ensure that shops with existing installations also have the uninstall webhook set up. In the following example, the RegisterWebhooksForActiveShop job is defined to iterate all shop records in the database and configure the webhooks.

Enqueue the background job

Enqueue the RegisterWebhooksForActiveShops background job to apply the webhook registration. For details on enqueuing ActiveJobs on Rails, refer to the Rails guides.

Verify the session token is being sent

Your app should now work using session token authentication. When any network calls are made, you should see the session token being sent in the header:

Next steps

  • Convert a multi-page, server-side rendered (SSR) app to use App Bridge authentication with Turbolinks.