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

React集成Microsoft登录:从服务端获取clientId始终为undefined排查

问题:React集成Microsoft登录时,从服务端获取clientId始终返回undefined

开发React应用集成Microsoft登录,尝试从服务端/api/config接口读取clientId,但clientId一直返回undefined。相关代码如下:

服务端代码(/server/server.ts)

app.get("/api/config/", async function (request: Request, response: Response) {
    const clientId = "abc-djfjgjg-gjjj-495"
    const config = {
        "clientId": clientId
    }
    response.status(200).send({
        ...config
    });
});

客户端代码

client/src/App.tsx

import { loginRequest, protectedResources } from "./config/config";

const App = () => {
  const authRequest = {
    ...loginRequest,
};

return (
      <>
       <MsalAuthenticationTemplate 
            interactionType={InteractionType.Redirect} 
            authenticationRequest={authRequest}
        >
        <HeaderComponent />
        <div className="bodycontainer">
          <MainPage/>
        </div>
        <FooterComponent/>
        </MsalAuthenticationTemplate>
      </>
  );
}

export default App;

client/src/config.tsx

let clientId : any ;
    const fetchData = async () => {
      const response = await fetch("/api/config/");
        const data = await response.json();
        clientId = data
}
//CALL FETCH DATA TO GET CONFIG DETAILS FROM SERVER
fetchData()
console.log("What is clientId" + clientId) // always returned as undefined

export const msalConfig = {
    auth: {
        clientId: 'xxxx-xxxxx-xxxxx-xxxx', 
        authority: 'https://login.microsoftonline.com/xxxxx-xxxxxx-xxx-xx',
        redirectUri: '/', 
        postLogoutRedirectUri: '/',
        clientCapabilities: ['CP1'],
    },
    cache: {
        cacheLocation: 'localStorage',
        storeAuthStateInCookie: false, 
    },
    system: {
        loggerOptions: {
            loggerCallback: (level : any, message : any, containsPii: any) => {
                if (containsPii) {
                    return;
                }
                switch (level) {
                    case LogLevel.Error:
                        console.error(message);
                        return;
                    case LogLevel.Info:
                        console.info(message);
                        return;
                    case LogLevel.Verbose:
                        console.debug(message);
                        return;
                    case LogLevel.Warning:
                        console.warn(message);
                        return;
                }
            },
        },
    },
};


export const protectedResources = {
    api: {
        firstEndpoint: 'http://localhost:3000/',
        dashboardEndpoint: 'http://localhost:3000/',
        scopes: ['api://xxxx-xxxxxx-x-xxxx/access_via_approle_assignments'],
    },
};

export const loginRequest = {
    scopes: [...protectedResources.api.scopes],
};

export const appRoles = {
    Railway: "Railway"
}

index.tsx

import { PublicClientApplication } from "@azure/msal-browser";
import { MsalProvider } from "@azure/msal-react";
import { msalConfig } from "./config/config";

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

const msalInstance = new PublicClientApplication(msalConfig);

root.render(
  <React.StrictMode>
      <BrowserRouter>
      <MsalProvider instance={msalInstance}>
              <App />
      </MsalProvider>
      </BrowserRouter>
  </React.StrictMode>
);

问题原因分析

  1. 异步执行顺序错误:fetchData是异步函数,调用后会立即执行后续的console.log,此时HTTP请求还未完成,clientId仍保持初始的undefined。
  2. msalConfig初始化时机错误:msalConfig在模块加载阶段就已创建,此时fetch请求还未返回结果,即使后续clientId被赋值,msalConfig中的clientId还是硬编码的固定值,不会自动更新。
  3. 数据解构错误:服务端返回的是{clientId: "abc-djfjgjg-gjjj-495"},但客户端直接把整个data对象赋值给clientId,正确的做法应该提取data.clientId字段。

解决方案

1. 修复数据赋值错误

先修改config.tsx中的fetchData函数,正确提取clientId:

const fetchData = async () => {
  const response = await fetch("/api/config/");
  const data = await response.json();
  clientId = data.clientId; // 改为提取clientId字段
}

2. 调整msal实例初始化时机

由于msalInstance需要在应用启动时就初始化,必须确保拿到clientId后再创建实例。将配置获取逻辑移到index.tsx中,用异步方式启动应用:

修改index.tsx:

import { PublicClientApplication, LogLevel } from "@azure/msal-browser";
import { MsalProvider } from "@azure/msal-react";
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import { protectedResources, loginRequest } from './config/config';

const initApp = async () => {
  // 1. 获取服务端配置
  const response = await fetch("/api/config/");
  const configData = await response.json();
  const clientId = configData.clientId;

  // 2. 创建msal配置
  const msalConfig = {
    auth: {
      clientId: clientId,
      authority: 'https://login.microsoftonline.com/xxxxx-xxxxxx-xxx-xx',
      redirectUri: '/',
      postLogoutRedirectUri: '/',
      clientCapabilities: ['CP1'],
    },
    cache: {
      cacheLocation: 'localStorage',
      storeAuthStateInCookie: false,
    },
    system: {
      loggerOptions: {
        loggerCallback: (level: any, message: any, containsPii: any) => {
          if (containsPii) return;
          switch (level) {
            case LogLevel.Error:
              console.error(message);
              return;
            case LogLevel.Info:
              console.info(message);
              return;
            case LogLevel.Verbose:
              console.debug(message);
              return;
            case LogLevel.Warning:
              console.warn(message);
              return;
          }
        },
      },
    },
  };

  // 3. 创建msal实例并渲染应用
  const msalInstance = new PublicClientApplication(msalConfig);
  const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);

  root.render(
    <React.StrictMode>
      <BrowserRouter>
        <MsalProvider instance={msalInstance}>
          <App />
        </MsalProvider>
      </BrowserRouter>
    </React.StrictMode>
  );
};

// 启动异步初始化流程
initApp();

3. 简化config.tsx

移除config.tsx中的fetch相关逻辑,改为导出基础配置项(供其他组件使用):

import { LogLevel } from "@azure/msal-browser";

export const protectedResources = {
  api: {
    firstEndpoint: 'http://localhost:3000/',
    dashboardEndpoint: 'http://localhost:3000/',
    scopes: ['api://xxxx-xxxxxx-x-xxxx/access_via_approle_assignments'],
  },
};

export const loginRequest = {
  scopes: [...protectedResources.api.scopes],
};

export const appRoles = {
  Railway: "Railway"
};

说明

  • 异步初始化流程确保msalInstance创建时已拿到最新的clientId,避免使用未初始化的值。
  • 修复数据提取错误后,能正确获取服务端返回的clientId字段。
  • 将配置逻辑集中到入口文件,保证实例创建时机与数据获取顺序一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 17:08:10