使用 Axios 进行 React-msal 的无效钩子调用问题

发布于 2025-01-16 23:45:36 字数 1045 浏览 0 评论 0原文

我正在我的应用程序中使用react-msal。我需要获取访问令牌并将其全局附加到 axios,但不幸的是,他们只提供挂钩来获取访问令牌(据我所知)。

到目前为止,这是我的 api.js 文件。

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;

这是我在组件中调用 API 的地方。

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

但我遇到一个问题:错误:无效的挂钩调用。钩子只能在函数组件的主体内部调用。这是显而易见的,但我需要替代方案来获取访问令牌并将其作为标头的一部分全局分配给我的 axios,这样我就不需要重写每次我需要调用端点时。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

怕倦 2025-01-23 23:45:36

您可以使用传递到 MsalProvider 中的 PublicClientApplication 实例。

要获取帐户,请调用 instance.getAllAccounts()

您无法在组件或上下文之外访问 inProgress 值,但由于您只是使用 acquireTokenSilent,因此您可能不需要它。

下面是我的工作样本。

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

和下面的index.js

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();

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();

御弟哥哥 2025-01-23 23:45:36

对于未来的读者来说,拉维的解决方案是有效的。
仅替换 export const msalInstance = new PublicClientApplication(msalConfig());
export const msalInstance = new PublicClientApplication(msalConfig);

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

夜夜流光相皎洁 2025-01-23 23:45:36

这是一个 React 应用程序,对吧?

您不能从 React 组件外部调用钩子或其他钩子。

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

输入图像描述这里

你可以这样做:

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

  useEffect(() => {
    instance.acquireTokenSilent()
    .then(() => {})
    .catch(() => {})
  },[]);
};

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(() => {})
  },[]);
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文