Consuming a Shopify Session Token with NodeJS

By Matthew Smith — Sun, 08 Nov 2020 — Permanent Link

DesignDevelopmentShopify

Learn how to consume a Shopify Session Token (JWT) with NodeJS.

Shopify recently released a powerful new feature to authenticate your embedded Shopify app using a session token.  The session token is built using a JWT (JSON Web Token) and sent over to your app using App Bridge.  We took advantage of this new feature with the latest release of Profit Pages to dramatically reduce app load times and get rid of the dreaded OAuth flash.  This makes our apps load faster and appear to be an extension of the Shopify admin.

Shopify provides some really good examples of using the new session token.  But, since they are based on Ruby on Rails, they were not a 1-to-1 match for the NodeJS/Lambda environment that we have setup.  So, I wanted to take a few minutes to explain how we consume Shopify session tokens using NodeJS and a stock JS web application.

Get the Session Token

Pull in the App bridge library into your project.  We do this in our index.html file.

<script src="https://unpkg.com/@shopify/app-bridge">

Start by getting the session token from the app bridge utils.  We are pulling the utils package into the app using the window object because we do not have a build process in our stock JS app.  This is done in our auth related client-side JavaScript file.

 // create app, redirect
 var createApp = AppBridge.createApp
 var utils = window['app-bridge-utils']
// create app const app = createApp({ apiKey: "[your-public-api-key]", shopOrigin: "[shop-name]" })
// try to get the session token try { const sessionToken = await utils.getSessionToken(app)
// exchange this for a app specific JWT decodeToken(sessionToken, success) } catch(e) {

// get token from OAuth getTokenFromOauth(success) }

If we successfully get the session token, then we will send it to our Lambda to be decoded.  That is up next.  If we are unable to get a session token, we will get the token from OAuth.  That is not shown in this post.

Decode your Token

Start by sending the JWT token you just decoded to the Lambda using a standard XHR call.  Pass the token in as the Bearer in your Authorization header.  This is a JWT best practice.  We have the Lambda setup behind a POST resource in API Gateway.  That is why we can access it using an XHR request.

 // setup data
let data = {}, apiUrl = '[your-api-url]'

// create a post to the api, the session var xhr = new XMLHttpRequest() xhr.open('POST', `${apiUrl}/decode-token`, true) xhr.setRequestHeader('Content-Type', 'application/json') xhr.setRequestHeader('Authorization', `Bearer ${sessionToken}`) xhr.send(JSON.stringify(data))
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 400) { console.log(`[debug] decodeToken success`)
let response = JSON.parse(xhr.responseText) } else { console.log(`[debug] decodeToken failure`) getTokenFromOauth(success) // not shown }

Now, in your AWS Lambda, decode the JWT with NodeJS.  To do this, you will need your app's shared secret and the JSON Web Token library.  The Shopify Session Token uses your app secret to encode the token.

const jwt = require('jsonwebtoken')

exports.handler = (event, context, callback) => {

let data = JSON.parse(event.body), jwtToken = event.headers.Authorization,
shopifySecretKey = '[your-shared-secret]'
// strip out bearer jwtToken = jwtToken.replace('Bearer ', '')

// validate jwt.verify(jwtToken, shopifySecretKey, function(err, decoded) {

// validate JWT if (err) sendError(callback, 'JWT invalid') else { console.log('JWT valid', decoded)
// get shop from token let shop = decoded.dest

})

}

That is about it!  The Shopify session provides some other information.  It is best practice to verify the token's timestamps prior to accepting it.  I also like to exchange the Shopify JWT for an app specific JWT that can be used to pass session related data among services.  This helps speed up our apps.