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

使用OidcClient+Azure AD获取AccessToken后调用userinfo API遇CORS错误

登录后调用Microsoft userinfo API遇CORS错误排查

问题描述

已通过下方React代码完成登录并获取Access Token,但调用https://login.microsoftonline.com/{tenant-id}/openid/userinfo API时触发CORS错误。

相关代码

import React, { useEffect, useState } from 'react';
import { OidcClient } from 'oidc-client';

const LoginButton = () => {
  const [user, setUser] = useState(null);

  const handleLogin = async () => {
    const config = {
      authority: 'https://login.microsoftonline.com/{tenant-id}',
      client_id: '{app-id}',
      redirect_uri: 'http://localhost:3000',
      response_type: 'code',
      scope: 'openid profile user.read',
    };

    const userManager = new OidcClient(config);
    const state = { nonce: generateNonce() };
    const request = await userManager.createSigninRequest({ state });

    saveState(state); // Persist the state to storage

    window.location.href = request.url;
  };

  useEffect(() => {
    const processCallback = async () => {
      const urlParams = new URLSearchParams(window.location.search);
      const code = urlParams.get('code');
      const savedState = getState(); // Retrieve the state from storage

      if (code && savedState) {
        const config = {
          authority: 'https://login.microsoftonline.com/{tenant-id}',
          client_id: '{app-id}',
          redirect_uri: "http://localhost:3000",
          response_type: 'code',
          scope: 'openid profile',
        };

        const userManager = new OidcClient(config);

        const response = await userManager.processSigninResponse();
        const userProfile = await userManager.getUser();

        setUser(userProfile);
        clearState(); // Clear the state from storage after successful authentication
      }
    };

    processCallback();
  }, []);

  const generateNonce = () => {
    // Implement your nonce generation logic here
    // For example, you can use a random string generator library
    return 'generated-nonce';
  };

  const saveState = (state) => {
    localStorage.setItem('authState', JSON.stringify(state));
  };

  const getState = () => {
    const savedState = localStorage.getItem('authState');
    return savedState ? JSON.parse(savedState) : null;
  };

  const clearState = () => {
    localStorage.removeItem('authState');
  };

  return (
    <div>
      {user && user.profile ? (
        <>
          <p>Welcome, {user.profile.given_name}!</p>
          <p>Access Token: {user.access_token}</p>
        </>
      ) : (
        <button onClick={handleLogin}>Login</button>
      )}
    </div>
  );
};

export default LoginButton;

错误原因分析

1. 端点选择错误:login.microsoftonline.com的userinfo端点不支持前端CORS

微软的https://login.microsoftonline.com/{tenant-id}/openid/userinfo端点不允许前端浏览器发起跨域请求,该端点设计用于后端服务调用。前端获取用户信息应改用Microsoft Graph的https://graph.microsoft.com/me端点,Graph API支持配置CORS规则。

2. 代码中的权限范围不一致

登录时请求的权限范围是openid profile user.read,但回调处理时的scope仅为openid profile,缺少user.read权限——该权限是调用Microsoft Graph /me端点的必要条件,同时也会影响oidc-client获取完整用户信息的能力。

3. Azure AD应用配置问题

  • 应用类型错误:若你的React应用是单页应用(SPA),需在Azure AD应用注册中将其配置为"单页应用"类型,而非"Web应用"。Web应用的授权码流默认不支持前端CORS,而SPA类型专门针对前端场景优化了CORS规则。
  • CORS来源未配置:在Azure AD应用注册的"跨域资源共享(CORS)"设置中,需添加http://localhost:3000作为允许的来源地址,确保前端请求被允许。
  • 权限未授予管理员同意:若user.read权限是应用权限而非委托权限,需确保已获取管理员同意,否则前端无法正常获取该权限对应的Access Token。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:07:47