Skip to content

Token fetched on every HTTP request #1

@OssiPesonen

Description

@OssiPesonen

I was monitoring my network calls when using your solution with React Admin and Azure AD B2C authentication. With your solution the access token gets fetched on every single HTTP request, again and again. This is due to the MSAL instance not being the same as what you pass down with React components and via props.

I went through the @azure/msal-react library and came up with the following solution:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { PublicClientApplication, InteractionType } from '@azure/msal-browser';
import { MsalProvider, MsalAuthenticationTemplate } from '@azure/msal-react';
import { msalConfig } from './authConfig';

const msalInstance = new PublicClientApplication(msalConfig);

ReactDOM.render(
  <React.StrictMode>
    <MsalProvider instance={msalInstance}>
      <MsalAuthenticationTemplate
        interactionType={InteractionType.Redirect}
      >
        <App />
      </MsalAuthenticationTemplate>
    </MsalProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();
// src/App.js
import * as React from 'react';
import { Admin, Resource } from 'react-admin';
import { CategoriesList } from './categories/CategoriesList';
import { ProductsList } from './products/ProductsList';
import LogoutButton from './LogoutButton';

import dataProvider from "./dataProvider";
import { useMsal, useAccount } from "@azure/msal-react";

const App = () => {
  const { instance, accounts } = useMsal();
  const account = useAccount(accounts[0] || {});

  React.useEffect(() => {
    if (account) {
      instance.acquireTokenSilent({
        scopes: ["openid", "offline_access"],
        account: account
      }).then((response) => {
        if(response) {
          // Always use this as the most recent access token
          localStorage.setItem("access_token", response.idToken);
        }
      });
    }
  }, [account, instance]);

  return (
    <Admin logoutButton={LogoutButton} dataProvider={dataProvider}>
      <Resource name="categories" list={CategoriesList} />
      <Resource name="products" list={ProductsList} />
    </Admin>
  );
}

Now as you use localStorage here, you always refresh the access token received from the MSAL instance, be it from cache or from a network call. You can then use it in yor httpClient.

const httpClient = async (url, options = {}) => {
  const accessToken = localStorage.getItem('access_token');

  if (accessToken) {
    if (!options.headers) {
      options.headers = new Headers({ Accept: 'application/json' });
    }

    options.headers.set('Authorization', `Bearer ${accessToken}`);
  }

  return fetchUtils.fetchJson(url, options);
};

My authConfig.js looks like this (little different with B2C I think):

// Config object to be passed to Msal on creation
export const msalConfig = {
  auth: {
    clientId: process.env.REACT_APP_CLIENT_ID,
    authority: `https://${process.env.REACT_APP_TENANT_NAME}.b2clogin.com/nicehomecashregisterapp.onmicrosoft.com/${process.env.REACT_APP_POLICY}`,
    knownAuthorities: [`${process.env.REACT_APP_TENANT_NAME}.b2clogin.com`],
    redirectUri: "http://localhost:3000/",
    postLogoutRedirectUri: "http://localhost:3000/",
    navigateToLoginRequestUrl: false,
  },
  cache: {
    cacheLocation: "localStorage", // This configures where your cache will be stored
    storeAuthStateInCookie: true, // Set this to "true" if you are having issues on IE11 or Edge
  }
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions