Azure Function调用Graph API时遇“Access Token缺失或格式错误”问题
问题描述
我正在开发一个与Azure Active Directory Graph API(@azure/graph)交互的Azure Function,用于验证邮箱是否在Azure AD(Entra ID)中已注册并验证。调用graphClient.users.list方法通过邮箱获取用户信息时,抛出以下错误:
使用Postman测试API
http://localhost:7071/api/checkEmail时的错误:Error verifying email: RestError: {"odata.error":{"code":"Authentication_MissingOrMalformed","codeForMetrics":"Authentication_MissingOrMalformed","message":{"lang":"en","value":"Access Token missing or malformed."}}}
已确认环境变量(AZURE_TENANT_ID、AZURE_CLIENT_ID、AZURE_CLIENT_SECRET)配置正确,尝试过DefaultAzureCredential和ClientSecretCredential,问题依旧,应用注册的Azure AD权限配置无异常。
相关代码如下:
const { GraphRbacManagementClient } = require("@azure/graph"); const { DefaultAzureCredential } = require("@azure/identity"); const tenantId = process.env.AZURE_TENANT_ID; module.exports = async function (context, req) { context.log('Checking if email is verified...'); const { email } = req.body; if (!email) { context.res = { status: 400, body: "Please provide the email" }; return; } try { const credential = new DefaultAzureCredential(); const graphClient = new GraphRbacManagementClient( credential, tenantId ); const verifyEmail = async (email) => { try { const users = await graphClient.users.list({ filter: `mail eq '${email}'` }); const user = users.next().value; if (user) { if (user.mailVerified) { return true; } else { return false; } } else { return false; } } catch (error) { console.error("Error verifying email:", error); return false; } }; const isEmailVerified = await verifyEmail(email); if (isEmailVerified) { context.res = { status: 200, body: "Email is verified" }; } else { context.res = { status: 400, body: "Email is not verified or does not exist" }; } } catch (error) { console.error("Error:", error); context.res = { status: 500, body: "Internal Server Error" }; } };
问题原因及解决方案
核心原因
@azure/graph(Azure AD Graph API)要求的令牌受众(audience)为https://graph.windows.net,但DefaultAzureCredential默认会请求Microsoft Graph API的令牌(受众为https://graph.microsoft.com),两者受众不匹配,导致Azure AD Graph API判定令牌格式错误或缺失。
解决方案
需要明确指定令牌的作用域为Azure AD Graph API的要求,具体有两种实现方式:
方法1:创建Graph客户端时指定作用域
修改GraphRbacManagementClient的初始化代码,添加scopes参数:
const graphClient = new GraphRbacManagementClient( credential, tenantId, { scopes: ["https://graph.windows.net/.default"] } );
方法2:使用ClientSecretCredential时直接指定作用域
如果使用ClientSecretCredential替代DefaultAzureCredential,可在初始化凭据时指定作用域:
const { ClientSecretCredential } = require("@azure/identity"); // 替换原DefaultAzureCredential初始化代码 const credential = new ClientSecretCredential( tenantId, process.env.AZURE_CLIENT_ID, process.env.AZURE_CLIENT_SECRET, { scopes: ["https://graph.windows.net/.default"] } ); const graphClient = new GraphRbacManagementClient(credential, tenantId);
额外检查点
- 确认应用注册已添加Azure AD Graph API的应用权限(而非Microsoft Graph),并完成管理员同意,常用权限为
User.Read.All。 - 本地调试时,可通过
console.log(process.env.AZURE_CLIENT_ID)验证环境变量是否正确加载。 - Azure Function作为后台服务,应使用应用权限而非用户委托权限,避免权限范围不匹配问题。
内容的提问来源于stack exchange,提问作者Snooper_A

