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

ASP.NET Core 6 Web API AAD授权调用Graph API报错咨询

问题根因

AADSTS28000是Azure AD的强制规则导致的:单次访问令牌请求仅允许对应一个目标资源(Audience),无法在同一次令牌申请中同时请求自定义Web API(资源标识为api://<Web API客户端ID>)和Microsoft Graph(资源标识为https://graph.microsoft.com)的权限,两类资源的令牌必须分开获取。
你之前在「公开API」板块添加客户端应用的操作仅用于实现权限预授权、跳过管理员同意流程,无法解决跨资源申请令牌的限制。

正确实现流程

你当前的架构是「公共客户端调用自定义Web API,自定义Web API再调用Microsoft Graph」,属于典型的代理调用场景,不能靠客户端一次性申请跨资源令牌实现,正确链路如下:

  • 客户端仅申请访问自定义Web API的令牌,scope参数只包含自定义API的EmployeeRecord.Read和openid、offline_access、profile等基础OpenID Connect scope,用这个令牌调用自定义Web API即可,这部分你已经跑通,不需要改动。
  • 自定义Web API收到请求中的Bearer令牌后,不直接用该令牌调用Graph,而是将该令牌作为用户身份凭证,通过OBO(On-Behalf-Of)流单独向AAD申请访问Microsoft Graph的专属令牌。
  • Web API拿到Graph专属令牌后,再携带该令牌调用Graph接口,就不会触发MsalUIRequiredException异常。
具体落地步骤

你创建项目时已经加了--calls-graph true参数,默认已经引入了Microsoft.Identity.Web和Graph SDK相关依赖,只需要补全配置和调用逻辑即可:

配置调整

  1. 权限配置修正:将Microsoft Graph的User.Read权限从公共客户端应用注册,转移到你的Web API应用注册中——因为实际调用Graph的主体是Web API,不是公共客户端,公共客户端不需要持有Graph权限。给该权限配置租户级管理员同意,避免调用时触发同意提示。
  2. 确认appsettings.json配置正确:
{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "company.onmicrosoft.com",
    "TenantId": "<替换为你的租户ID>",
    "ClientId": "<替换为你的Web API客户端ID>",
    "ClientSecret": "<替换为你的Web API客户端密钥,使用证书认证则替换为对应证书配置>",
    "Scopes": "EmployeeRecord.Read"
  },
  "GraphApi": {
    "BaseUrl": "https://graph.microsoft.com/v1.0",
    "Scopes": "User.Read"
  }
}
  1. Program.cs中补全服务注入,开启OBO流和Graph客户端支持:
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// 注册JWT认证,开启下游API调用的令牌获取能力,注入Graph客户端
builder.Services.AddAuthentication(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddMicrosoftGraph(builder.Configuration.GetSection("GraphApi"))
    .AddInMemoryTokenCaches();

builder.Services.AddControllers();
// 其他原有服务注入逻辑保持不变

var app = builder.Build();

// 中间件逻辑保持原有配置即可,确保认证授权中间件顺序正确
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

接口调用示例

在需要调用Graph的控制器中直接注入GraphServiceClient即可,SDK底层会自动完成OBO流令牌兑换、令牌缓存的全流程,不需要手动编写令牌申请逻辑:

using Microsoft.AspNetCore.Authorization;
using Microsoft.Graph;
using Microsoft.Identity.Web.Resource;

[ApiController]
[Route("api/employee")]
[RequiredScope("EmployeeRecord.Read")] // 校验访问当前接口的令牌必须包含该scope
public class EmployeeController : ControllerBase
{
    private readonly GraphServiceClient _graphClient;

    public EmployeeController(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }

    [HttpGet("current-user")]
    public async Task<IActionResult> GetCurrentUser()
    {
        // 自动用当前请求的用户令牌兑换Graph访问令牌后发起调用
        var user = await _graphClient.Me.GetAsync();
        return Ok(new
        {
            DisplayName = user.DisplayName,
            Email = user.Mail
        });
    }
}
避坑说明
  • 绝对不要在客户端的令牌请求中同时拼接两个不同资源的scope,这是AAD的硬性限制,没有绕过方式。
  • 不要让客户端直接拿Graph令牌调用你的自定义Web API,两种令牌的Audience校验规则不同,必然会返回401未授权。
  • 开发环境如果用InMemory令牌缓存,服务重启后缓存会清空,生产环境建议替换为分布式缓存(如Redis)存储令牌。

内容的提问来源于stack exchange,提问作者ezG

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 16:12:28