Using Azure/Entra Authentication in React Js with Msal Library

Simplified MSal Authentication in React js.


Install the required libraries

pnpm install @azure/msal-react @azure/msal-browser


Create config file to keep all config

auth/msal-config.ts
import { LogLevel, type Configuration, type PopupRequest, type SilentRequest } from "@azure/msal-browser";
import appConfig from "../app-config";

export const msalConfig: Configuration = {
  auth: {
    clientId: "your_client_id",
    authority: "https://login.microsoftonline.com/{your_tenant_id}",
    redirectUri: appConfig.appUrl,
  },
  cache: {
    cacheLocation: "localStorage",
    storeAuthStateInCookie: false,
  },
  system: {
    loggerOptions: {
      logLevel: LogLevel.Info,
    },
  },
  // add more options if needed
};

export const appScopes = ["App Scopes"];

auth/useMsalAuth.ts
import { useIsAuthenticated, useMsal } from "@azure/msal-react";
import { InteractionRequiredAuthError } from "@azure/msal-browser";
import { appScopes } from "./msal-config";

export default function useMsalAuth() {
  const { instance, accounts } = useMsal();
  const isAuthenticated = useIsAuthenticated();

  const HOME_ACCOUNT_ID = "homeAccountId";
  const ACCOUNT_USERNAME = "accountUsername";

  // Function to handle login
  const handleLogin = async () => {
    try {
      const response = await instance.loginPopup({
        scopes: appScopes,
      });
      if (response.account) {
        instance.setActiveAccount(response.account);
        localStorage.setItem(HOME_ACCOUNT_ID, response.account.homeAccountId); // save the homeAccountId in local storage
        localStorage.setItem(ACCOUNT_USERNAME, response.account.username); // save the username in local storage
      }
    } catch (error) {
      console.error(error);
    }
  };

  // Function to handle logout
  const handleLogout = async () => {
    await instance.logoutPopup({
      account: getAccount(),
    });
    localStorage.removeItem(HOME_ACCOUNT_ID);
    localStorage.removeItem(ACCOUNT_USERNAME);
  };

  // Function to access token when making api calls
  const getAccessToken = async (): Promise<string | null> => {
    try {
      const authenticationResult = await instance.acquireTokenSilent({
        scopes: appScopes,
        account: getAccount(),
      });
      return authenticationResult.accessToken;
    } catch (error) {
      if (error instanceof InteractionRequiredAuthError) {
        // fallback to interaction when silent call fails
        const authenticationResult = await instance.acquireTokenPopup({
          scopes: appScopes,
          account: getAccount(),
        });

        return authenticationResult.accessToken;
      }
      return null;
    }
  };

  // function to get The active account
  const getAccount = () => {
    const homeAccountId = localStorage.getItem(HOME_ACCOUNT_ID);
    const accountUsername = localStorage.getItem(ACCOUNT_USERNAME);

    if (!homeAccountId || !accountUsername) {
      return;
    }
    const account = accounts.find((a) => a.homeAccountId === homeAccountId && a.username === accountUsername);
    return account;
  };

  // function to check user roles
  const hasRole = (role: string): boolean => {
    const account = getAccount();
    if (!account) {
      return false;
    }

    const roles = account.idTokenClaims?.roles || [];
    return roles.includes(role);
  };

  return {
    isAuthenticated,
    handleLogin,
    handleLogout,
    getAccessToken,
    getAccount,
    hasRole,
  };
}

Protect Routes

Now we can use this hook to protect routes.

import { useIsAuthenticated } from "@azure/msal-react";
import { Navigate } from "react-router-dom";

export const ProtectedRoute = ({ children }) => {
  const { isAuthenticated } = useMsalAuth();

  if (!isAuthenticated) {
    return <Navigate to="/" />;
  }

  return children;
};

use handleLogin, handleLogout, getAccessToken for login, logout and get the access token to make api calls to backend.

const { handleLogin, handleLogout, getAccessToken } = useMsalAuth();

use getAccount, hasRole to check user info and user roles

const { getAccount, hasRole } = useMsalAuth();