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.

options.ts
// apps/storefront-unified-nextjs/sdk/options.ts
import type { CreateSdkOptions } from '@vue-storefront/next';
import { env } from 'next-runtime-env';

export function getSdkOptions() {
  const apiUrl = env('NEXT_PUBLIC_ALOKAI_MIDDLEWARE_API_URL') ?? '';
  const ssrApiUrl = env('NEXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL');
  const cdnCacheBustingId =
    env('NEXT_PUBLIC_ALOKAI_MIDDLEWARE_CDN_CACHE_BUSTING_ID') ?? env('GIT_SHA') ?? 'no-cache-busting-id-set';
  const isMultiStoreEnabled = env('NEXT_PUBLIC_MULTISTORE_ENABLED') === 'true';
  if (!apiUrl) {
    throw new Error('NEXT_PUBLIC_ALOKAI_MIDDLEWARE_API_URL is required to run the app');
  }

  const options: CreateSdkOptions = {
    middleware: {
      apiUrl,
      cdnCacheBustingId,
      ssrApiUrl,
    },
    multistore: {
      enabled: isMultiStoreEnabled,
    },
  };

  return options;
}

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, create a refresh token handler using an errorHandler config property.

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.

// apps/storefront-unified-nextjs/sdk/config.ts
import { createRefreshTokenAndRetryHandler } from '@vsf-enterprise/sapcc-sdk'; import { isSdkUnauthorizedError } from '@vue-storefront/sdk'; import { defineSdkConfig } from '@vue-storefront/next';
import type { UnifiedEndpoints } from 'storefront-middleware/types';

const handleRefreshTokenAndRetry = createRefreshTokenAndRetryHandler(); 
export function getSdkConfig() {
  return defineSdkConfig(({ buildModule, config, getRequestHeaders, middlewareModule }) => ({
    // ...
    unified: buildModule(middlewareModule<UnifiedEndpoints>, {
      apiUrl: `${sdkConfig.middlewareUrl}/commerce/unified`,
      cdnCacheBustingId: config.cdnCacheBustingId,
      defaultRequestConfig: {
        headers: getRequestHeaders(),
      },
      errorHandler: async (context) => {
        const { error, params, httpClient, config: errorHandlerConfig, url } = context;
        return handleRefreshTokenAndRetry(error, () => httpClient(url, params, errorHandlerConfig), params, {
          isUnauthorized(err) {
            return isSdkUnauthorizedError(err) && err.message.includes('InvalidTokenError');
          },
          refreshTokenMethod: async () => {
            await httpClient(`${config.middlewareUrl}/commerce/OAuthUserTokenRefresh`, []);
          },
        });
      },
      methodsRequestConfig: config.defaultMethodsRequestConfig.unifiedCommerce.middlewareModule,
    }),
  }));
}