You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

页面刷新导致MSAL React实例初始化失败问题排查

问题描述

我正在开发一个使用MSAL的React应用,流程为用户重定向到登录页面,认证成功后返回应用。HomePage.tsx加载时会触发第三方API调用,需使用MSAL生成的访问令牌,首次运行一切正常。

但登录成功后刷新页面时,调用msalInstance.acquireTokenSilent()会失败,报错如下:

uninitialized_public_client_application
You must call and await the initialize function before attempting to call any other MSAL API.

首次登录返回应用后,msalInstance已完成初始化,可正常获取访问令牌;但刷新页面后,msalInstance处于未初始化状态。

对比另一个同流程但运行正常的React应用,唯一差异是MSAL版本:
正常应用依赖版本:

"@azure/msal-browser": "^2.32.2",
"@azure/msal-react": "^1.5.2",

当前新应用依赖版本:

"@azure/msal-browser": "^3.5.0",
"@azure/msal-react": "^2.0.7",

想了解msal-react v2是否需要不同的处理逻辑?是否遗漏了关键步骤导致页面刷新后MSAL实例未初始化?


相关代码

main.tsx

const msalConfig: Configuration = {
  auth: {
    clientId: config.clientId,
    authority: config.authority
  }
}

const pca = new PublicClientApplication(msalConfig);

const authRequest = {
  scopes: ["openid", "profile"]
};

root.render(
    <MsalProvider instance={pca}>
      <MsalAuthenticationTemplate interactionType={InteractionType.Redirect} authenticationRequest={authRequest}>
        <App msalInstance={pca} msalConfig={config}/>
      </MsalAuthenticationTemplate>
      <UnauthenticatedTemplate>
        <p>No users are signed in!</p>
      </UnauthenticatedTemplate>
    </MsalProvider>
)

App.tsx

function App(props: any) {
    const { instance, accounts, inProgress } = useMsal();

    const [bookmarkedApps, setBookmarkedApps] = useState<UserApp[]>();

    const getAccessToken = async () => {
        try{
            const msalResponse = await props.msalInstance.acquireTokenSilent({
                scopes: [props.msalConfig.scopes],
                account: accounts[0]
            });
            return msalResponse.accessToken;
        } catch (error) {
            console.log(error!.errorCode);
            console.log(error!.errorMessage);
        }
        
    }

        
    const getBookmarkedApps = async () => {
        const resp = await fetch('/api/apps/bookmarked', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer ' + await getAccessToken()
            },
            body: JSON.stringify({
                environmentConfigId: 1
            })
        });

        if(resp.ok) {
            const data = await resp.json();
            setBookmarkedApps(data);
        }
    }

    const router = createBrowserRouter([
        {
            path: '/',
            element: <RootLayout />,
            children: [
                { path: '/', element: <HomePage getAccessToken={getAccessToken} bookmarkedApps={bookmarkedApps}/> },
                {
                    path: '/apps', element: <Outlet />, children: [
                        { path: '/apps/manage', element: <ManageApps /> },
                    ]
                }
            ]
        },
    ]);

    useEffect(() => {
        getBookmarkedApps();
    }, []);

    return (
        <RouterProvider router={router} />
    );
}

export default App;

package.json依赖

"dependencies": {
    "@azure/msal-browser": "^3.5.0",
    "@azure/msal-react": "^2.0.7",
    "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    "@mui/icons-material": "^5.14.18",
    "@mui/material": "^5.14.18",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router": "^6.20.0",
    "react-router-dom": "^6.20.0"
  }

解决方案

MSAL.js v3(对应msal-react v2)的核心变更之一是:PublicClientApplication实例必须显式调用并等待initialize()方法完成后,才能调用其他MSAL API,而v2版本中实例是自动初始化的,这是导致刷新后报错的根本原因。

具体修复步骤

  1. 修改main.tsx,添加异步初始化逻辑
    创建MSAL实例后,需先调用await pca.initialize(),再渲染应用。由于React 18的root.render是同步操作,需用异步函数包裹:

    const msalConfig: Configuration = {
      auth: {
        clientId: config.clientId,
        authority: config.authority
      }
    }
    
    const pca = new PublicClientApplication(msalConfig);
    
    // 异步初始化MSAL实例后再渲染应用
    async function renderApp() {
      await pca.initialize();
      root.render(
          <MsalProvider instance={pca}>
            <MsalAuthenticationTemplate interactionType={InteractionType.Redirect} authenticationRequest={{ scopes: ["openid", "profile"] }}>
              <App msalConfig={config}/>
            </MsalAuthenticationTemplate>
            <UnauthenticatedTemplate>
              <p>No users are signed in!</p>
            </UnauthenticatedTemplate>
          </MsalProvider>
      );
    }
    
    renderApp();
    
  2. 优化App.tsx中MSAL实例的使用
    无需通过props传递msalInstance,直接使用useMsal()返回的instance,符合msal-react的最佳实践:

    const getAccessToken = async () => {
        try{
            // 使用useMsal返回的instance,替代props传递的实例
            const msalResponse = await instance.acquireTokenSilent({
                scopes: [props.msalConfig.scopes],
                account: accounts[0]
            });
            return msalResponse.accessToken;
        } catch (error) {
            console.log(error!.errorCode);
            console.log(error!.errorMessage);
        }
    }
    
  3. 避免在MSAL初始化过程中触发API调用
    利用useMsal()返回的inProgress状态,仅当MSAL无正在进行的操作且已有登录账户时,再调用API:

    useEffect(() => {
        // 确保MSAL初始化完成且用户已登录
        if (inProgress === InteractionStatus.None && accounts.length > 0) {
            getBookmarkedApps();
        }
    }, [inProgress, accounts]);
    

原因说明

MSAL.js v3将初始化从同步改为异步,目的是提升页面加载性能和实例初始化的可靠性。如果跳过initialize()调用,实例会一直处于未初始化状态,页面刷新后所有依赖MSAL的操作都会触发报错。而v2版本中实例自动完成初始化,因此旧应用无此问题。


内容的提问来源于stack exchange,提问作者LNX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 20:00:57