How to handle token expiry in azure msal react? – Azure

by
Liam Thompson
azure-active-directory azure-ad-msal msal-react reactjs

Quick Fix: Adjust token checking interval to reduce frequency of token checks and trigger silent token renewal when token is about to expire or has expired.

The Problem:

In a React application utilizing Azure Single Sign-On (SSO), the developer is encountering difficulties managing token expiration and renewal. Despite implementing a token checking and refresh mechanism, the application continues to use an expired token, leading to 401 errors from the API. The developer seeks guidance on the proper approach to handle token expiration and obtain new tokens in Azure MSAL React.

The Solutions:

Solution 1: Improved Token Expiry Handling

The provided solution effectively addresses the issue of token expiry and renewal in Azure MSAL React. Here’s an improved version of the solution with additional explanations and code optimizations:

Improved Code

import { useEffect, useRef } from 'react';
import { useMsal } from '@azure/msal-react';
import jwt from 'jwt-decode';
import { backendScopeRequest } from '../config/authConfig';
import { useAuthStore } from '../store/auth';

const REFRESH_THRESHOLD = 300; // 5 minutes in seconds

export const useBackendTokenCheckExpirationTime = () => {
  const interval = useRef(null);
  const { instance, accounts } = useMsal();
  const { updateBackendAccessToken, updateLoggedInUserProfile } = useAuthStore();

  const acquireTokenWithRefreshToken = async () => {
    try {
      if (accounts.length && instance) {
        const response = await instance.acquireTokenSilent({
          account: accounts[0],
          ...backendScopeRequest,
        });
        const decodeToken = jwt(response.accessToken);
        localStorage.setItem('backendAccessToken', response.accessToken);
        updateBackendAccessToken(response.accessToken);
        updateLoggedInUserProfile(decodeToken);
        console.log('Token refreshed');
        console.log('Token renewed:', decodeToken);
      }
    } catch (error) {
      console.log('Error refreshing token', error); // Handle token refresh error
    }
  };

  useEffect(() => {
    const checkTokenExpiry = () => {
      const backendAccessToken = localStorage.getItem('backendAccessToken');
      if (backendAccessToken) {
        const decodeToken = jwt(backendAccessToken);
        const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
        const timeUntilExpiry = decodeToken.exp - currentTime;
        if (timeUntilExpiry <= REFRESH_THRESHOLD) { // Token is about to expire or has expired, refresh it
          acquireTokenWithRefreshToken();
        }
      }
    };

    interval.current = setInterval(checkTokenExpiry, 3000); // Check token expiry every 3 seconds

    // Check token expiry immediately after mounting
    checkTokenExpiry();

    // Clear interval on cleanup
    return () => clearInterval(interval.current);
  }, []);

  return null; // This hook does not need to return anything
};

Improvements:

  • Reduced Token Checking Frequency:

    • The token check interval is set to 3 seconds, which is more appropriate for production environments. Frequent token checks can impact performance.
  • Immediate Token Check:

    • The token expiry is checked immediately after mounting the component to ensure that the token is valid from the start.
  • Simplified Return Value:

    • The hook does not need to return anything, as it only performs side effects (token expiry checking and renewal).

Usage:

To use this hook, simply import it into your React component and call it within a useEffect hook:

import { useBackendTokenCheckExpirationTime } from './path/to/hook';

const MyComponent = () => {
  useBackendTokenCheckExpirationTime();

  return (
    <div>
      {/* Your component logic */}
    </div>
  );
};

With these improvements, you can effectively handle token expiry and renewal, ensuring that your React application continues to have a valid token for accessing protected resources.

Q&A

How to renew an expired token in Azure MSAL React?

Call acquireTokenWithRefreshToken when the token expires.

What are the key changes you made to the hook to ensure proper token handling?

Increased token checking interval, added immediate token expiry check.

What should you check to determine if a token needs to be refreshed?

Compare the token’s expiry time to the current time with a threshold.

Video Explanation:

The following video, titled "Using MSAL.js to integrate React Single-page applications with ...", provides additional insights and in-depth exploration related to the topics discussed in this post.

Play video

This month's in-depth topic: Deep dive on using MSAL.js to integrate React Single-page applications with Azure Active Directory.