A guide to JWTs in BigCommerce

There are a few scenarios where you will come across JWTs as a BigCommerce developer. We’ll run through a few different examples in this article to give you confidence on how to handle them, including:

If you’re unsure of the fundamentals of JWTs, read our introduction to JWTs before we get started.

BigCommerce GraphQL API JWT

The BigCommerce storefront GraphQL API is authenticated using a JWT. When building a stencil theme, the simplest way of retrieving a valid access token is using the one provided you with the stencil context:

{{settings.storefront_api.token}}

Fore more advanced use cases, you can also generate a new token, including specifying the expiry date and the allowed domains:

POST https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token
x-auth-token: {{access_token}}
accept: application/json
content-type: application/json
{
 "channel_id": 1,            
 "expires_at": 1602288000,   
 "allowed_cors_origins": [ "https://example.com" ]
}

You can then send an authenticated GraphQL request with the token in the Authorization header

curl 'https://store.com/graphql' -H 'authorization: Bearer {token}'

GraphQL API responses will be provided in the context of the current customer. If you need to access information about other customers, you will need to impersonate them.

Impersonating customers

To request information in the context of any customer using the storefront API, you will need to pass an additional header:

curl 'https://store.com/graphql' 
    -H 'authorization: Bearer {token}'
    -H 'x-bc-customer-id: 123'

This request should only be made server-side and requires a more privileged JWT. One can be generated using the Management API:

POST https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token-customer-impersonation
x-auth-token: {{access_token}}
accept: application/json
content-type: application/json
{
 "channel_id": 1, 
 "expires_at": 1602288000 
}

Bear in mind that any frontend API that uses customer impersonation should ensure that clients cannot access sensitive information about another customer. One way to achieve this is using the Current Customer API to retrieve a JWT that can be used to verify the currently logged-in customer.

Current Customer API JWT

When modifying a stencil-based storefront, it can be useful to identify the currently logged-in customer. This is particularly useful when building applications that add third-party JavaScript to the page that need to know the customer to customise the experience, e.g. a wishlist app.

BigCommerce provides the Current Customer API for this purpose. A request to /current/customer.jwt will receive a JWT identifying the logged-in customer. For example, our Metafields Manager app provides an API to access order metafields as they are unavailable through GraphQL. To integrate with this API, first fetch the current customer:

const app_client_id = "c0s7a28bku7ij79qte07ibtdedu619x"; // Metafields Manager Client ID
const fetchCustomerJWT = async (app_client_id) => {
  const response = await fetch("/customer/current.jwt?app_client_id=" + app_client_id);
  if (!response.ok) {
    if (response.status === 404) {
      return new Error("Customer not logged in.");
    }
    return new Error("Unable to identify customer");
  }
  return await response.text();
};

// eyJhbGciOiJSUzI1NiJ9.eyJpZCI6…
const jwt = fetchCustomerJWT(app_client_id);

// Send to backend

On the server, our application can then use the provided JWT to authenticate & authorize access to order metafields:

import jwt from "jsonwebtoken";

const payload = jwt.verify(
  jwtString,
  process.env.BC_APP_CLIENT_SECRET, 
  {
    algorithms: ["HS256"],
    audience: process.env.BC_APP_CLIENT_ID,
  }
);

Headless authentication

When a customer logs in on a headless storefront, they’ll often authenticate with an API on a separate domain. Once authenticated the API may return a JWT to be sent as a method of authorization in all further API requests. This is often chosen for simplicity. It avoids the need for managing sessions using server-side session storage like Redis, and developers also don’t need to worry about cross-domain cookie issues like CORS.

A JWT-based flow will see the single-page application receive a JWT on successful authentication with the API. Unlike a traditional cookie-based auth flow, the JWT will need to be manually stored somewhere to ensure it’s retained on page reload, and it will also need to be attached to all further API requests.

Security considerations

The challenge with JWT persistence is how to do so securely. One downside of the stateless approach of JWTs is that anyone who can recover the JWT can impersonate a customer until that JWT expires. For that reason, JWTs often have relatively short life spans compared to cookie-based sessions.

Where possible it’s recommended that JWTs are set in a cookie by the server. This ensures that no third-party JavaScript can read (and steal) the JWT. Additionally, as browsers automatically send cookies to matching domains, it simplifies the process of authenticating API requests.

Conversely, storing the JWT in the URL, local storage or a client-side cookie would make the app susceptible to cross-site scripting (XSS) attacks.

Tom Robertshaw
Stay up to date with Hypa news and features