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

Next.js调用MS Graph API获令牌遇AADSTS9002326错误求助

问题分析与解决建议

核心问题原因

你使用的client_credentials(客户端凭证)模式是服务端到服务端的认证方式,绝对不能在浏览器端(Next.js前端代码)直接调用:

  • 浏览器端请求会直接暴露client_secret,存在严重安全风险
  • Azure AD的token端点禁止前端跨域请求该模式的token,这就是报错AADSTS9002326的根本原因
  • Postman和Azure Function能正常工作,是因为它们属于服务端环境,没有跨域限制,且可以安全存储client_secret

Next.js 解决方案

方案1:用Next.js API路由做中间层(推荐)

把获取token的逻辑放到Next.js的服务端API路由中,前端调用自己的API路由,由服务端发起请求到Azure AD,避免跨域和密钥暴露。

步骤1:创建API路由

比如在pages/api/get-graph-token.ts(Pages Router)或者app/api/get-graph-token/route.ts(App Router)中编写:

// Pages Router 示例
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const tenantId = process.env.AZURE_TENANT_ID;
  const clientId = process.env.AZURE_CLIENT_ID;
  const clientSecret = process.env.AZURE_CLIENT_SECRET;
  const scope = 'https://graph.microsoft.com/.default';

  try {
    const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: clientId!,
        client_secret: clientSecret!,
        scope: scope,
      }),
    });

    const data = await response.json();
    if (!response.ok) {
      throw new Error(data.error_description || 'Failed to get token');
    }

    res.status(200).json({ accessToken: data.access_token });
  } catch (error) {
    res.status(500).json({ error: (error as Error).message });
  }
}

步骤2:前端调用该API路由

修改你的前端代码,请求自己的API而不是直接调用Azure的token端点:

const { post, data, error, loading } = useFetch<any>('/api/get-graph-token');

useEffect(() => {
  const timeoutId = setTimeout(() => void post({}), 200);
  return () => clearTimeout(timeoutId);
}, []);

const accessToken = data?.accessToken || "key not found";

return { post, data, accessToken, error, loading };

步骤3:配置环境变量

在.env.local中存储敏感信息,不要硬编码:

AZURE_TENANT_ID=你的租户ID
AZURE_CLIENT_ID=你的客户端ID
AZURE_CLIENT_SECRET=你的客户端密钥

方案2:使用SPA专用的PKCE授权流程(不推荐用于服务端权限场景)

如果你的场景是用户授权(而非服务端到服务端的后台权限),可以使用SPA专用的PKCE授权码流程,无需client_secret,用@azure/msal-react库实现:

  • 确保Azure AD应用类型设置为Single-page application
  • 用MSAL React初始化认证上下文,通过授权码流程获取用户上下文的token,再调用Graph API

但注意:该模式用于用户交互场景,无法获取服务端级别的全量权限,仅能获取当前用户授权的权限。

关键注意事项

  • 永远不要在前端代码中暴露client_secret,这会导致你的Azure AD应用被滥用
  • client_credentials模式仅适用于服务端环境,包括Next.js API路由、Server Components、Azure Function等
  • 若调整Azure AD应用类型,需确保对应的认证流程匹配:Web应用对应授权码流程(带客户端密钥),SPA应用对应PKCE流程(无客户端密钥)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 00:30:45