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

调用Azure AD Web API遇403授权失败:token含scp却不符角色要求

问题:调用受保护的Web API时出现HTTP 403错误

问题背景

运行微软Web API示例项目,完成官方文档所有配置步骤后,调用API仍收到HTTP 403错误。

排查细节

  • 开启全量日志追踪,配置如下:
"Logging": {
  "LogLevel": {
    "Default": "Trace",
    "Microsoft.AspNetCore": "Trace"
  }
}
  • 日志显示授权失败:

info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed. These requirements were not met:
RolesAuthorizationRequirement:User.IsInRole must be true for one of the following roles: (Forecast.Read)

  • 通过jwt.io验证token,确认包含scp声明,值为Forecast.Read
  • 移除代码中的.RequireAuthorization("AuthZPolicy")后,API可正常调用

相关代码片段

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("weatherForecast")
.RequireAuthorization("AuthZPolicy"); // Protect this endpoint with the AuthZPolicy

原因及解决方法

核心原因

你的AuthZPolicy授权策略被配置为基于角色的校验规则,但token中携带的是OAuth2范围(Scope)声明scp。ASP.NET Core默认不会将scp声明识别为角色,因此触发RolesAuthorizationRequirement校验失败。

解决方式

方式1:修改授权策略为范围校验

直接将策略从角色要求改为范围要求,匹配token中的scp声明:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AuthZPolicy", policy =>
        policy.RequireScope("Forecast.Read"));
});

方式2:将scp声明映射为角色

如果需要保留角色校验逻辑,可以通过自定义Claims转换,把token中的scp值添加为角色声明:

// 注册认证服务时添加自定义ClaimsPrincipalFactory
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
    .AddClaimsPrincipalFactory<ScopeToRoleClaimsFactory>();

// 自定义Claims转换类
public class ScopeToRoleClaimsFactory : MicrosoftIdentityClaimsPrincipalFactory
{
    public ScopeToRoleClaimsFactory(IAzureADOptionsMonitor<MicrosoftIdentityOptions> optionsAccessor, ILogger<MicrosoftIdentityClaimsPrincipalFactory> logger)
        : base(optionsAccessor, logger)
    {
    }

    protected override async Task<ClaimsPrincipal> GenerateClaimsPrincipalAsync(TokenValidatedContext context)
    {
        var principal = await base.GenerateClaimsPrincipalAsync(context);
        var scopeClaim = context.SecurityToken.Claims.FirstOrDefault(c => c.Type == "scp")?.Value;
        
        if (!string.IsNullOrEmpty(scopeClaim))
        {
            foreach (var scope in scopeClaim.Split(' '))
            {
                ((ClaimsIdentity)principal.Identity).AddClaim(new Claim(ClaimTypes.Role, scope));
            }
        }
        
        return principal;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 23:44:57