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
相关产品推荐
相关产品推荐

