Vue Storefront is now Alokai! Learn More
Automatically Refreshing Access Tokens

Automatically Refreshing Access Tokens

Learn how to automatically refresh access tokens for logged-in users in the Alokai Storefront and Unified SAP Integration

This guide is for Storefronts using SAP Commerce Cloud

If you are using another integration that has a different token renewal process, please refer to the documentation for that integration.

A common challenge in modern frontend applications is the security of the authentication process.

Among the many considerations in the selection process of solutions is the lifespan of a logged-in user's token. Some opt for very restrictive solutions with a short lifespan for the access token. But it's not a great user experience to have to reauthenticate often because your access token expires quickly.

In this guide, we'll implement automatic handling for renewing the access token for requests that fail due to this reason.

Prerequisites

  • Alokai Storefront with the configured Middleware SDK Module
  • Unified SAP Integration configured in the middleware

Base SDK Configuration

For reference, here is the standard starting configuration of the Alokai Starter's middlewareModule.

Next.js
// apps/storefront-unified-nextjs/sdk/sdk.config.ts
import { contentfulModule } from '@vsf-enterprise/contentful-sdk';
import { CreateSdkOptions, createSdk } from '@vue-storefront/next';
import { UnifiedEndpoints } from 'storefront-middleware/types';

if (!process.env.NEXT_PUBLIC_API_BASE_URL) {
  throw new Error('NEXT_PUBLIC_API_BASE_URL is required to run the app');
}

const options: CreateSdkOptions = {
  middleware: {
    apiUrl: process.env.NEXT_PUBLIC_API_BASE_URL,
  },
  multistore: {
    enabled: process.env.NEXT_PUBLIC_MULTISTORE_ENABLED === 'true',
  },
};

export const { getSdk } = createSdk(options, ({ buildModule, middlewareModule, middlewareUrl, getRequestHeaders }) => ({
  unified: buildModule(middlewareModule<UnifiedEndpoints>, {
    apiUrl: `${middlewareUrl}/commerce`,
    defaultRequestConfig: {
      headers: getRequestHeaders(),
    },
  }),
  cms: buildModule(contentfulModule, {
    apiUrl: `${middlewareUrl}/cntf`,
  }),
}));

export type Sdk = ReturnType<typeof getSdk>;

Implementing the Token Refreshing Function

At a high level, the process of refreshing the access token involves creating a custom error handler that will attempt to refresh the access token when a request fails due to an unauthorized error.

The @vsf-enterprise/sapcc-sdk package provides a function that handles the access token renewal process and retrying requests. This function is called createRefreshTokenAndRetryHandler.

First, install the @vsf-enterprise/sapcc-sdk package.

yarn add @vsf-enterprise/sapcc-sdk

Next, using the createRefreshTokenAndRetryHandler function, we can create a refresh token handler and create an error handler.

If the request fails due to an unauthorized error, the error handler will call the refresh token handler to attempt to renew the access token and retry the request.

Next.js
// apps/storefront-unified-nextjs/sdk/sdk.config.ts
import { contentfulModule } from '@vsf-enterprise/contentful-sdk';
import { createRefreshTokenAndRetryHandler } from '@vsf-enterprise/sapcc-sdk';
import { CreateSdkOptions, createSdk } from '@vue-storefront/next';
import { isSdkUnauthorizedError } from '@vue-storefront/sdk';
import { UnifiedEndpoints } from 'storefront-middleware/types';

if (!process.env.NEXT_PUBLIC_API_BASE_URL) {
  throw new Error('NEXT_PUBLIC_API_BASE_URL is required to run the app');
}

const options: CreateSdkOptions = {
  middleware: {
    apiUrl: process.env.NEXT_PUBLIC_API_BASE_URL,
  },
  multistore: {
    enabled: process.env.NEXT_PUBLIC_MULTISTORE_ENABLED === 'true',
  },
};

const handleRefreshTokenAndRetry = createRefreshTokenAndRetryHandler();

export const { getSdk } = createSdk(options, ({ buildModule, middlewareModule, middlewareUrl, getRequestHeaders }) => ({
  unified: buildModule(middlewareModule<UnifiedEndpoints>, {
    apiUrl: `${middlewareUrl}/commerce`,
    defaultRequestConfig: {
      headers: getRequestHeaders(),
    },
    errorHandler: async (context) => {
      const { error, params, httpClient, config, url } = context;
      return handleRefreshTokenAndRetry(error, () => httpClient(url, params, config), params, {
        isUnauthorized(err) {
          return isSdkUnauthorizedError(err) && err.message.includes('InvalidTokenError');
        },
        refreshTokenMethod: async () => {
          await httpClient(`${middlewareUrl}/commerce/OAuthUserTokenRefresh`, []);
        },
      });
    },
  }),
  cms: buildModule(contentfulModule, {
    apiUrl: `${middlewareUrl}/cntf`,
  }),
}));

export type Sdk = ReturnType<typeof getSdk>;