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

使用@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);
问题分析与解决方案
  1. 客户端凭证模式仅支持应用权限
    你使用的是ConfidentialClientApplication客户端凭证流,该模式下委托权限完全不生效,仅应用权限会被纳入令牌权限范围,配置的委托权限可忽略,重点检查应用权限的配置与授权状态。

  2. 作用域的正确用法
    客户端凭证模式下,无法直接使用UserAuthenticationMethod.Read或UserAuthenticationMethod.Read.All作为作用域,必须使用https://graph.microsoft.com/.default——该作用域会自动包含所有已授予的应用权限。提示无效作用域属于正常现象,因为具体权限值并非客户端凭证流的合法作用域格式。

  3. 403错误核心排查点

    • 确认应用权限已完成管理员同意:
      登录Azure AD门户,检查应用注册的「API权限」中,UserAuthenticationMethod.Read.All权限的状态是否为「已授予[租户名]管理员同意」,若为「需要管理员同意」,必须点击「授予管理员同意」按钮完成授权。
    • 验证令牌权限:
      将获取到的accessToken通过JWT解析工具(可使用本地工具或在线解析器,注意不要输入敏感数据)解析,查看roles字段是否包含UserAuthenticationMethod.Read.All。若未包含,说明权限未正确纳入令牌,需重新检查权限配置与授权流程。
    • 确认权限范围:
      确保UserAuthenticationMethod.Read.All权限是全局范围,而非针对特定用户组的限制权限,否则可能无法访问目标用户的认证方法数据。
    • 检查租户与用户状态:
      确认目标用户存在于当前租户中,且应用未被租户条件访问策略限制访问该用户的认证方法数据。
  4. 代码优化建议
    代码逻辑本身无问题,可添加令牌权限验证步骤,提前确认权限是否正确:

    // 需先安装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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 20:07:05