Node.js使用@azure/msal-node调用Azure订阅API报AuthenticationFailed
故障原因
调用列订阅接口返回AuthenticationFailed,核心原因是提交给Azure资源管理器(ARM)的访问令牌未通过服务端校验,高频触发场景如下:
- 令牌受众不匹配:ARM接口仅接受
aud声明值为https://management.azure.com的访问令牌,若实际拿到的令牌是为Microsoft Graph等其他服务签发的,即使令牌本身合法也会被拒绝。 - 授权流程未完成:配置的
https://management.azure.com/user_impersonation为委托权限,需要登录用户或租户管理员完成显式授权同意,否则令牌不会携带对应权限声明。 - 登录端点配置问题:当前代码中使用
https://login.microsoftonline.com/organizations通用端点,若登录账号为个人微软账号(MSA)、或登录时走错租户,会导致令牌签发租户与ARM预期不符。 - 请求格式错误:比如误将id token作为access token传递、请求头中
Bearer前缀格式错误、令牌已过期。
排查步骤
按以下顺序校验可快速定位问题:
- 解码获取到的access token,检查payload内的两个核心字段:
- 校验
aud字段值:必须为https://management.azure.com,如果值为其他地址(比如https://graph.microsoft.com),说明换token时scope配置错误,拿到的是其他资源的令牌。 - 校验
scp字段值:必须包含user_impersonation,如果缺失说明授权同意流程未走完。
- 校验
- 补全并检查授权码回调的换token逻辑(当前仅贴出了授权URL生成代码,缺少换token部分):换token时传入的scope必须和授权URL阶段的scope完全一致,不要遗漏ARM相关权限,也不要同时混传多个不同资源的scope——授权码流默认仅为第一个资源签发令牌,混传多资源scope会导致非首个资源的权限缺失。
- 校验接口请求头格式:调用ARM接口时请求头必须为
Authorization: Bearer <access-token>,注意Bearer后保留半角空格,禁止传入id token。 - 检查API权限配置状态:

进入应用注册的API权限页,确认已添加Azure服务管理下的user_impersonation权限,且权限状态显示已完成授予同意,没有待同意的提示。
修复方案
- 若使用单租户场景,将配置中的authority改为租户专属端点:
https://login.microsoftonline.com/<你的租户ID>,替换掉通用的organizations端点,避免个人账号、错租户登录导致的令牌异常。 - 测试阶段先将scope简化为仅保留
["https://management.azure.com/user_impersonation"],调通ARM接口调用后,再通过增量同意流程追加user.read等其他资源的权限,不要在首次授权时混传多资源scope。 - 若使用
https://management.azure.com/.default作为scope测试,必须提前在API权限页配置好所需的ARM权限,并完成租户管理员同意,否则.default不会自动拉取未授权的权限。 - 最后确认登录账号本身在目标Azure订阅中已分配至少
Reader及以上角色:user_impersonation仅代表应用获得代表用户调用ARM的许可,不代表账号本身拥有订阅访问权限(注意:账号无权限通常返回AuthorizationFailed而非AuthenticationFailed,可最后排查此项)。
修正后的参考配置代码:
const config = { auth: { clientId: 'xxx', // 单租户替换为你的租户ID,多租户场景可保留organizations但要做租户校验 authority: 'https://login.microsoftonline.com/<你的租户ID>', clientSecret: 'yyy', }, system: { loggerOptions: { loggerCallback(loglevel, message, containsPii) { console.log(message); }, piiLoggingEnabled: false, logLevel: msal.LogLevel.Verbose, }, }, }; // 初始化MSAL confidential client实例 const pca = new msal.ConfidentialClientApplication(config); const app = express(); app.get('/', (req, res) => { const authCodeUrlParameters = { // 测试阶段仅保留ARM权限,调通后再追加其他资源权限 scopes: ['https://management.azure.com/user_impersonation'], redirectUri: REDIRECT_URI, }; // 获取授权登录URL pca .getAuthCodeUrl(authCodeUrlParameters) .then((response) => { res.redirect(response); }) .catch((error) => console.log(JSON.stringify(error))); });
接口返回的典型错误示例:
{ "error": { "code": "AuthenticationFailed", "message": "Authentication failed." } }
内容的提问来源于stack exchange,提问作者vishalsaugat
相关产品推荐
相关产品推荐

