MSAL Authentication in React: Complete Guide – Part 4: Token Management and API Calls

MSAL Authentication in React: Complete Guide – Part 4: Token Management and API Calls

This entry is part 4 of 5 in the series Mastering MSAL Authentication in React Applications

Welcome to Part 4 of our MSAL authentication series! Now that we have protected routes in place, let’s dive into token management and making authenticated API calls to secure endpoints.

Understanding MSAL Tokens

MSAL handles three types of tokens in your application:

  • ID Token: Contains user identity information
  • Access Token: Used for calling protected APIs
  • Refresh Token: Used to acquire new access tokens

Acquiring Access Tokens

Let’s create a custom hook for token acquisition:

// src/hooks/useAccessToken.js
import { useState, useCallback } from 'react';
import { useMsal } from '@azure/msal-react';
import { InteractionRequiredAuthError } from '@azure/msal-browser';

export const useAccessToken = () => {
  const { instance, accounts } = useMsal();
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const getAccessToken = useCallback(async (scopes = ['User.Read']) => {
    if (accounts.length === 0) {
      throw new Error('No accounts found');
    }

    setIsLoading(true);
    setError(null);

    const request = {
      scopes,
      account: accounts[0],
    };

    try {
      // Try to acquire token silently
      const response = await instance.acquireTokenSilent(request);
      setIsLoading(false);
      return response.accessToken;
    } catch (err) {
      if (err instanceof InteractionRequiredAuthError) {
        // If silent acquisition fails, fall back to interactive
        try {
          const response = await instance.acquireTokenRedirect(request);
          setIsLoading(false);
          return response?.accessToken;
        } catch (interactiveError) {
          setError(interactiveError);
          setIsLoading(false);
          throw interactiveError;
        }
      } else {
        setError(err);
        setIsLoading(false);
        throw err;
      }
    }
  }, [instance, accounts]);

  return { getAccessToken, isLoading, error };
};

Making Authenticated API Calls

Create a reusable hook for making authenticated API calls:

// src/hooks/useApi.js
import { useState, useCallback } from 'react';
import { useMsal } from '@azure/msal-react';
import { InteractionRequiredAuthError } from '@azure/msal-browser';

export const useApi = () => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const { instance, accounts } = useMsal();

  const callApi = useCallback(async (url, scopes = ['User.Read'], options = {}) => {
    if (accounts.length === 0) {
      throw new Error('No authenticated accounts');
    }

    setLoading(true);
    setError(null);

    try {
      // Get access token
      const tokenRequest = {
        scopes,
        account: accounts[0],
      };

      const response = await instance.acquireTokenSilent(tokenRequest);
      const accessToken = response.accessToken;

      // Make API call
      const apiResponse = await fetch(url, {
        ...options,
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json',
          ...options.headers,
        },
      });

      if (!apiResponse.ok) {
        throw new Error(`API call failed: ${apiResponse.status}`);
      }

      const result = await apiResponse.json();
      setLoading(false);
      return result;
    } catch (err) {
      if (err instanceof InteractionRequiredAuthError) {
        // Redirect to acquire token interactively
        instance.acquireTokenRedirect({
          scopes,
          account: accounts[0],
        });
      } else {
        setError(err);
        setLoading(false);
        throw err;
      }
    }
  }, [instance, accounts]);

  return { callApi, loading, error };
};

Microsoft Graph Integration

Let’s create a component that fetches user data from Microsoft Graph:

// src/components/UserProfile.js
import React, { useState, useEffect } from 'react';
import { useApi } from '../hooks/useApi';

const UserProfile = () => {
  const [userData, setUserData] = useState(null);
  const { callApi, loading, error } = useApi();

  useEffect(() => {
    const fetchUserData = async () => {
      try {
        const data = await callApi('https://graph.microsoft.com/v1.0/me', ['User.Read']);
        setUserData(data);
      } catch (err) {
        console.error('Failed to fetch user data:', err);
      }
    };

    fetchUserData();
  }, [callApi]);

  if (loading) return <div>Loading user profile...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!userData) return <div>No user data available</div>;

  return (
    <div className="user-profile">
      <h2>User Profile</h2>
      <div className="profile-details">
        <p><strong>Name:</strong> {userData.displayName}</p>
        <p><strong>Email:</strong> {userData.mail || userData.userPrincipalName}</p>
        <p><strong>Job Title:</strong> {userData.jobTitle || 'Not specified'}</p>
        <p><strong>Department:</strong> {userData.department || 'Not specified'}</p>
      </div>
    </div>
  );
};

export default UserProfile;

Token Caching Best Practices

  • Use minimal scopes: Request only the permissions you need
  • Handle token expiration: Implement proper retry logic
  • Cache tokens securely: Use sessionStorage over localStorage
  • Monitor token refresh: Log token acquisition events

What’s Next?

In Part 5, our final installment, we’ll cover advanced topics and production considerations including security best practices, performance optimization, and troubleshooting common issues.

Navigate<< MSAL Authentication in React: Complete Guide – Part 3: Protected Routes and Route GuardsMSAL Authentication in React: Complete Guide – Part 5: Advanced Topics and Production Considerations >>

Written by:

265 Posts

View All Posts
Follow Me :
How to whitelist website on AdBlocker?

How to whitelist website on AdBlocker?

  1. 1 Click on the AdBlock Plus icon on the top right corner of your browser
  2. 2 Click on "Enabled on this site" from the AdBlock Plus option
  3. 3 Refresh the page and start browsing the site