1

I'm using react-msal to my application. I need to acquire the access token and attach it to the axios globally, but unfortunately, they only provide hooks to get the access token (as far as I know).

So far, here's my api.js file.

import axios from "axios";
import { useMsal } from "@azure/msal-react";
const axiosInstance = axios.create({
  baseURL: "https://localhost:4211/api",
});

const { instance, accounts } = useMsal();

instance
  .acquireTokenSilent({
    ...loginApiRequest,
    account: accounts[0],
  })
  .then((response) => {
    axiosInstance.defaults.headers.common[
      "Authorization"
    ] = `Bearer ${response.accessToken}`;
  })
  .catch((error) => {
    console("Error acquiring access token");
  });

export default axiosInstance;

And here's I call my API in my component.

api.get('/foods').then(response => {
    alert(response.data)
}).catch(error => {
    console.log(error.response)
})

But I'm getting an issue that says: Error: Invalid hook call. Hooks can only be called inside of the body of a function component. which is obvious but I need alternatives to get the access token and assign it to my axios globally as part of the header so I don't need to rewrite header each time I need to call an endpoints. Any help?

wowew
  • 11
  • 2
  • `useMsal` hook is called in a component underneath MsalProvider. issue point out here https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-react/docs/errors.md – Mohit Sharma Oct 03 '22 at 09:28

3 Answers3

0

You can use PublicClientApplication instance passed into the MsalProvider.

To get the accounts call instance.getAllAccounts().

You can't access the inProgress value outside of a component or context, but since you're just using acquireTokenSilent you probably will not need it.

below is my working sample.

import axios from 'axios';
import * as App from '../index'
import * as utils from './utils'

const instance = axios.create({
    baseURL: utils.getEndpoint(),
    timeout: 15000
});

instance.interceptors.request.use(function (config) {

    const instance = App.msalInstance;
    const accounts = instance.getAllAccounts();

    const accessTokenRequest = {
        scopes: ["user.read"],
        account: accounts[0],
    };

    return instance
        .acquireTokenSilent(accessTokenRequest)
        .then((accessTokenResponse) => {
            // Acquire token silent success
            let accessToken = accessTokenResponse.accessToken;
            // Call your API with token
            config.headers.Authorization = `Bearer ${accessToken}`;
            return Promise.resolve(config)
        })
}, function (error) {
    return Promise.reject(error);
});

instance.interceptors.response.use((response) => {
    if(response.status === 401) {
        // Clear local storage, redirect back to login
        window.location.href = "/logout"
    }
    return response;
}, (error) => {
    return Promise.reject(error);
});

export default instance

and index.js below

import React from "react";
import ReactDOM from "react-dom";

import { PublicClientApplication, EventType } from "@azure/msal-browser";
import { msalConfig } from "./authConfig";

import App from "./App";
import * as serviceWorker from "./serviceWorker";

export const msalInstance = new PublicClientApplication(msalConfig());

// Default to using the first account if no account is active on page load
if (!msalInstance.getActiveAccount() && msalInstance.getAllAccounts().length > 0) {
  // Account selection logic is app dependent. Adjust as needed for different use cases.
  msalInstance.setActiveAccount(msalInstance.getAllAccounts()[0]);
}

// Optional - This will update account state if a user signs in from another tab or window
msalInstance.enableAccountStorageEvents();

msalInstance.addEventCallback((event) => {
  if (event.eventType === EventType.LOGIN_SUCCESS && event.payload.account) {
    const account = event.payload.account;
    msalInstance.setActiveAccount(account);
  }
});

ReactDOM.render(<App pca={msalInstance} />,
  document.getElementById("app"),
);

serviceWorker.unregister();
0

for future readers, Ravi's solution works. Only replace export const msalInstance = new PublicClientApplication(msalConfig()); with export const msalInstance = new PublicClientApplication(msalConfig);

  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/34046311) – pigrammer Mar 22 '23 at 23:58
-1

This is a React application, right?

You can't call hooks from outside of your React components, or other hooks.

https://reactjs.org/docs/hooks-rules.html

enter image description here

You could do something like this:

const App = () => {
  const { instance, accounts } = useMsal();

  useEffect(() => {
    instance.acquireTokenSilent()
    .then(() => {})
    .catch(() => {})
  },[]);
};
cbdeveloper
  • 27,898
  • 37
  • 155
  • 336
  • 4
    Yes, this is react. Yes, that's my original code, being inside the component to be able to achieve the `accessToken` but I need to get the access token from msal instance in the `api.js` file. I'm not sure how to achieve the `accessToken` without using their given hooks which is the `const { instance, accounts } = useMsal();` Because I want to set the header globally with token attach to it. – wowew Mar 26 '22 at 10:38
  • Could you find some solution to it? – Alvin Aug 17 '22 at 04:51