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

Duende Identity Server改用数据库后缺失Profile声明问题求助

Duende Identity Server切换EF数据库模式后缺失Google IDP的Profile声明问题

问题背景

测试Duende Identity Server的6_JS_with_backend示例对接Google IDP时,内存模式运行正常;切换为Entity Framework数据库模式后,返回的声明中缺失name、givenname、familyname等Profile相关信息,切回内存模式则恢复正常。数据库已通过EF迁移成功创建,无执行错误,JavaScript客户端未做任何修改。

相关代码

ConfigureServices配置代码

public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
{
    var migrationsAssembly = typeof(Program).Assembly.GetName().Name;

    builder.Services.AddRazorPages();
    string connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
    builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));

    builder.Services.AddIdentity<ApplicationUser, ApplicationRole>()
      .AddEntityFrameworkStores<ApplicationDbContext>();

    builder.Services.AddIdentityServer(options =>
    {
        options.EmitStaticAudienceClaim = true;
    })
        .AddConfigurationStoreCache()
        .AddConfigurationStore(options =>
        {
            options.EnablePooling = true;
            options.ConfigureDbContext = builder =>
            {
                builder.UseSqlServer(connectionString, configure =>
                {
                    configure.MigrationsAssembly(migrationsAssembly);
                });
            };
        })
        .AddOperationalStore(options =>
        {
            options.EnablePooling = true;
            options.ConfigureDbContext = builder =>
            {
                builder.UseSqlServer(connectionString, configure =>
                {
                    configure.MigrationsAssembly(migrationsAssembly);
                });
            };

            options.EnableTokenCleanup = true;
            options.TokenCleanupInterval = (int)TimeSpan.FromDays(20).TotalSeconds;
        });
    builder.Services.AddIdentityServerConfiguration(opt => opt.LicenseKey = "<TEST>").AddClientConfigurationStore();
    builder.Services.AddConfigurationDbContext<ConfigurationDbContext>(options =>
    {
        options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString);
    });

    var configuration = builder.Configuration;
    // Put in Keyvault
    builder.Services.AddAuthentication()
        .AddGoogle("Google", options =>
        {
            options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
            options.ClientId = configuration["Authentication:Google:ClientId"]!;
            options.ClientSecret = configuration["Authentication:Google:ClientSecret"]!;
        });      

    return builder.Build();
}

ApplicationRole类

using Microsoft.AspNetCore.Identity;

namespace IdentityServer.Models
{
    public class ApplicationRole : IdentityRole
    {
        
    }
}

ApplicationUser类

using Microsoft.AspNetCore.Identity;

namespace IdentityServer.Models
{
    public class ApplicationUser : IdentityUser
    {
        public string? TenantId { get; set; }
        public string? License { get; set; }
    }
}

结果与数据库截图

  • 内存模式下返回结果:
    内存模式下包含Profile声明的结果

  • 数据库模式下返回结果:
    数据库模式下缺失Profile声明的结果

  • 数据库配置截图(client、clientscopes、identityresources已存在):
    数据库中存在客户端和资源配置的截图

问题分析与解决办法

核心原因

内存模式下IdentityServer默认会自动加载标准Identity资源(如profile)并包含对应声明,但切换到数据库模式后,需确保数据库中IdentityResources的配置完整,且客户端已授权对应范围。

解决步骤

  1. 检查IdentityResources的UserClaims配置
    查看数据库IdentityResources表中Profile资源的UserClaims字段,确认是否包含name、given_name、family_name等声明类型。若缺失,需手动添加这些声明到该资源的UserClaims集合中。

  2. 确认客户端AllowedScopes包含profile范围
    检查Clients表中对应客户端的AllowedScopes字段,确保已添加profile范围,否则IdentityServer不会返回该范围下的声明。

  3. 显式配置Google认证的声明映射
    修改Google认证配置,显式请求Profile相关范围并映射声明:

    .AddGoogle("Google", options =>
    {
        options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
        options.ClientId = configuration["Authentication:Google:ClientId"]!;
        options.ClientSecret = configuration["Authentication:Google:ClientSecret"]!;
        // 显式请求Profile范围
        options.Scope.Add("profile");
        // 映射Google返回的JSON声明到标准Claim类型
        options.ClaimActions.MapJsonKey(JwtClaimTypes.GivenName, "given_name");
        options.ClaimActions.MapJsonKey(JwtClaimTypes.FamilyName, "family_name");
        options.ClaimActions.MapJsonKey(JwtClaimTypes.Name, "name");
    });
    
  4. 初始化标准Identity资源到数据库
    若数据库未自动初始化标准资源,可在应用启动时添加种子数据:

    using (var scope = app.Services.CreateScope())
    {
        var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
        if (!context.IdentityResources.Any(r => r.Name == "profile"))
        {
            var profileResource = new IdentityResource("profile", "User Profile", new List<string> 
            { 
                JwtClaimTypes.Name, 
                JwtClaimTypes.GivenName, 
                JwtClaimTypes.FamilyName 
            });
            context.IdentityResources.Add(profileResource.ToEntity());
            context.SaveChanges();
        }
    }
    
  5. 配置Identity的Claim类型映射
    确保ASP.NET Core Identity使用标准JWT Claim类型:

    builder.Services.Configure<IdentityOptions>(options =>
    {
        options.ClaimsIdentity.UserNameClaimType = JwtClaimTypes.Name;
        options.ClaimsIdentity.GivenNameClaimType = JwtClaimTypes.GivenName;
        options.ClaimsIdentity.FamilyNameClaimType = JwtClaimTypes.FamilyName;
    });
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 21:50:18