使用@azure/msal-node调用GraphAPI获取用户认证方法遇403错误
问题描述
- 使用MSAL客户端凭证模式获取Graph API令牌,作用域设为
https://graph.microsoft.com/.default,调用v1.0/users/{userPrincipalName}/authentication/methods接口时返回403错误 - 已配置并授予以下权限:
- 委托权限:User.Read、User.Read.All、UserAuthenticationMethod.Read、UserAuthenticationMethod.Read.All
- 应用权限:UserAuthenticationMethod.Read.All
- 尝试将
https://graph.microsoft.com/UserAuthenticationMethod.Read和https://graph.microsoft.com/UserAuthenticationMethod.Read.All作为作用域时,提示无效作用域
相关代码
获取令牌代码
const msalConfig = { auth: { clientId: clientId, privateKey: this.configService .get('AZURE_APP_PEM') .split('|') .join('\n'), x5c: this.configService.get('AZURE_APP_CERT').split('|').join('\n'), thumbprint: this.configService.get('AZURE_APP_THUMBPRINT'), authority: aadEndpoint + '/' + tenantId, clientSecret: clientSecret, }, }; const cca = new msal.ConfidentialClientApplication(msalConfig); const foundToken = await cca.acquireTokenByClientCredential({ scopes: [ 'https://graph.microsoft.com/.default' ], });
API调用代码
async callApi(api: string, tenantId: string) { const graphEndpoint = this.configService.get('AZURE_GRAPH_ENDPOINT'); const token = await this.getToken(tenantId); const options = { headers: { Authorization: `Bearer ${token?.accessToken}`, }, }; let data = []; try { let response = await axios.get(`${graphEndpoint}/${api}`, options); data.push( ...response.data.value); while( typeof response.data['@odata.nextLink'] === "string" && response.data['@odata.nextLink'].length > 0 ) { response = await axios.get(response.data['@odata.nextLink'], options); data.push( ...response.data.value); } return data; } catch (error) { console.log(error); return error; } }
调用语句
await this.callApi(`v1.0/users/${user.userPrincipalName}/authentication/methods`, user.tenantId);
问题分析与解决方案
客户端凭证模式仅支持应用权限
你使用的是ConfidentialClientApplication客户端凭证流,该模式下委托权限完全不生效,仅应用权限会被纳入令牌权限范围,配置的委托权限可忽略,重点检查应用权限的配置与授权状态。作用域的正确用法
客户端凭证模式下,无法直接使用UserAuthenticationMethod.Read或UserAuthenticationMethod.Read.All作为作用域,必须使用https://graph.microsoft.com/.default——该作用域会自动包含所有已授予的应用权限。提示无效作用域属于正常现象,因为具体权限值并非客户端凭证流的合法作用域格式。403错误核心排查点
- 确认应用权限已完成管理员同意:
登录Azure AD门户,检查应用注册的「API权限」中,UserAuthenticationMethod.Read.All权限的状态是否为「已授予[租户名]管理员同意」,若为「需要管理员同意」,必须点击「授予管理员同意」按钮完成授权。 - 验证令牌权限:
将获取到的accessToken通过JWT解析工具(可使用本地工具或在线解析器,注意不要输入敏感数据)解析,查看roles字段是否包含UserAuthenticationMethod.Read.All。若未包含,说明权限未正确纳入令牌,需重新检查权限配置与授权流程。 - 确认权限范围:
确保UserAuthenticationMethod.Read.All权限是全局范围,而非针对特定用户组的限制权限,否则可能无法访问目标用户的认证方法数据。 - 检查租户与用户状态:
确认目标用户存在于当前租户中,且应用未被租户条件访问策略限制访问该用户的认证方法数据。
- 确认应用权限已完成管理员同意:
代码优化建议
代码逻辑本身无问题,可添加令牌权限验证步骤,提前确认权限是否正确:// 需先安装jwt-decode包:npm install jwt-decode import jwtDecode from 'jwt-decode'; const foundToken = await cca.acquireTokenByClientCredential({ scopes: [ 'https://graph.microsoft.com/.default' ], }); // 验证令牌是否包含所需角色 const decodedToken = jwtDecode(foundToken.accessToken); if (!decodedToken.roles?.includes('UserAuthenticationMethod.Read.All')) { throw new Error('令牌未包含UserAuthenticationMethod.Read.All权限'); }
内容的提问来源于stack exchange,提问作者Tim Holum
相关产品推荐
相关产品推荐

