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

Identity Server+Azure AD+Blazor声明(Claim)传递异常问题排查

解决Identity Server 4与Azure AD集成后Claim缺失的问题

看起来你遇到的核心问题是IS4没有把从AAD获取的完整声明传递到Blazor应用的id_token里。结合你的配置和日志,我们可以从以下几个方向排查修复:

1. 确保AAD向IS4返回所需的所有声明

首先,你的IS4作为AAD的依赖方,需要正确配置以请求并接收AAD的完整声明。调整AddOpenIdConnect的配置:

services.AddAuthentication()
    .AddOpenIdConnect("aad", "Sign-in with Azure AD", options => {
        options.Authority = "https://login.microsoftonline.com/common";
        options.ClientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
        options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
        options.SignOutScheme = IdentityServerConstants.SignoutScheme;
        options.ResponseType = "id_token";
        options.CallbackPath = "/signin-aad";
        options.SignedOutCallbackPath = "/signout-callback-aad";
        options.RemoteSignOutPath = "/signout-aad";
        // 新增:请求AAD的profile和email scope,确保获取相关声明
        options.Scope.Add("profile");
        options.Scope.Add("email");
        // 如果需要角色声明,添加roles scope(前提是AAD应用配置了角色)
        options.Scope.Add("roles");
        // 开启从UserInfo端点获取额外声明(AAD有些声明不会默认放在id_token里)
        options.GetClaimsFromUserInfoEndpoint = true;
        options.TokenValidationParameters = new TokenValidationParameters {
            ValidateIssuer = false,
            NameClaimType = "name",
            RoleClaimType = "role"
        };
        // 新增:映射AAD的声明到标准Claim类型,避免命名不一致
        options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
        options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
        options.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
        options.ClaimActions.MapJsonKey(ClaimTypes.Role, "role");
    });

2. 配置IS4的Identity资源,确保声明被允许传递

你的config.cs里需要明确定义包含所需声明的Identity资源,否则IS4不会将这些声明包含在id_token中:

public static IEnumerable<IdentityResource> IdentityResources =>
    new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(), // 包含name、family_name、given_name等声明
        new IdentityResources.Email(),   // 包含email、email_verified声明
        new IdentityResource(
            name: "roles",
            displayName: "User Roles",
            userClaims: new List<string> { "role" }
        )
    };

同时,确保你的客户端AllowedScopes包含这些资源:

AllowedScopes = { "openid", "profile", "email", "roles", "backend" },

3. 自定义Profile Service,确保外部声明被正确转发

默认的IS4 Profile Service不会自动将所有外部身份源(AAD)的声明传递给客户端。你需要实现IProfileService来手动包含这些声明:

public class CustomProfileService : IProfileService
{
    private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
    private readonly UserManager<ApplicationUser> _userManager;

    public CustomProfileService(UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory)
    {
        _userManager = userManager;
        _claimsFactory = claimsFactory;
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var sub = context.Subject.GetSubjectId();
        var user = await _userManager.FindByIdAsync(sub);
        if (user == null)
        {
            throw new ArgumentException("Invalid subject identifier");
        }

        var principal = await _claimsFactory.CreateAsync(user);
        var claims = principal.Claims.ToList();

        // 额外添加从外部身份源(AAD)获取的声明
        var externalClaims = context.Subject.FindAll(c => c.Type != ClaimTypes.NameIdentifier).ToList();
        claims.AddRange(externalClaims);

        // 确保只返回请求的声明类型
        var requestedClaims = context.RequestedClaimTypes;
        if (requestedClaims.Any())
        {
            claims = claims.Where(c => requestedClaims.Contains(c.Type)).ToList();
        }

        context.IssuedClaims = claims;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        var sub = context.Subject.GetSubjectId();
        var user = await _userManager.FindByIdAsync(sub);
        context.IsActive = user != null;
    }
}

然后在Startup.cs中注册这个服务:

services.AddScoped<IProfileService, CustomProfileService>();

4. 检查Azure AD应用注册的令牌配置

登录Azure Portal,找到你的AAD应用注册:

  • 进入令牌配置,点击添加声明,确保你需要的声明(比如email、given_name、family_name、role等)被添加并设置为在id_token中返回。
  • 如果使用角色,需要在应用角色或企业应用程序中给用户分配角色,确保AAD会将角色声明包含在令牌中。

5. 调试验证声明流转

启用IS4的详细日志,查看从AAD获取的原始声明:

  • 在appsettings.json中设置日志级别:
    "Logging": {
      "LogLevel": {
        "Default": "Debug",
        "Microsoft": "Warning",
        "Microsoft.Hosting.Lifetime": "Information",
        "IdentityServer4": "Debug"
      }
    }
    
  • 查看日志中外部认证后的ClaimsPrincipal,确认AAD是否返回了所需的声明。如果AAD没返回,就需要调整AAD应用配置;如果AAD返回了但IS4没传递,就需要检查Profile Service和Identity资源配置。

按照这些步骤调整后,应该就能让所有请求的声明出现在id_token中了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 20:57:58