首页 > 解决方案 > httpcontext IdentityServer4 中有时不存在自定义声明

问题描述

我们有一个问题,有时是 HttpContext 中不存在自定义声明。

    public async Task<ActionResult<IEnumerable<Entity>>> GetAsync()
    {
        var customClaims = this.HttpContext.User.Claims // custom claims are not present

        return Ok();
    }

应该:

在此处输入图像描述

结果:

在此处输入图像描述

我们的配置:

我们客户的登录名和密码

    public IEnumerable<Client> GetClients()
    {
        return new[]
        {
            new Client
            {
                ClientId = "anonym-spa",
                ClientName = "anonym spa client",
                AllowedGrantTypes = GrantTypes.CodeAndClientCredentials,
                ClientSecrets = { new Secret(_options.CurrentValue.ClientSecret.Sha256()) },
                RequireConsent = false,
                RequirePkce = true,

                RedirectUris = GetRedirectUris(),
                PostLogoutRedirectUris = GetPostLogoutRedirectUris(),

                AllowedCorsOrigins = GetAllowedCorsOrigins(),
                AlwaysIncludeUserClaimsInIdToken = true,
                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "anonym-api",
                    "oidc"
                },
                AllowOfflineAccess = true,
                AllowPlainTextPkce = false,
                AllowAccessTokensViaBrowser = true,
                AccessTokenLifetime = 1209600,
            }
        };
    }

获取发现文件

    public async Task<TokenResponse?> GetToken()
    {
        var disco = await _httpClient.GetDiscoveryDocumentAsync().ConfigureAwait(false);
        if (disco.IsError)
        {
            return null;
        }

        // request token
        using var tokenRequest = new ClientCredentialsTokenRequest
        {
            Address = disco.TokenEndpoint,

            ClientId = _options.CurrentValue.ClientId,
            ClientSecret = _options.CurrentValue.ClientSecret,
            Scope = _options.CurrentValue.Scope
        };

        var tokenResponse = await _httpClient.RequestClientCredentialsTokenAsync(tokenRequest).ConfigureAwait(false);
        if (tokenResponse.IsError)
        {
            return null;
        }

        return tokenResponse;
    }

客户

    public void ConfigureServices(IServiceCollection services)
    {
        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = "cookie";
            options.DefaultSignInScheme = "cookie";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("cookie")
        .AddOpenIdConnect("oidc", options =>
        {
            options.Authority = Configuration.GetValue<string>("identity:Authority");
            options.RequireHttpsMetadata = true;
            options.ClientId = Configuration.GetValue<string>("identity:ClientId");
            options.ClientSecret = Configuration.GetValue<string>("identity:ClientSecret");
            options.ResponseType = "code";
            options.ResponseMode = "form_post";
            options.UsePkce = true;

            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("anonym-api");
            options.Scope.Add("offline_access");
            options.GetClaimsFromUserInfoEndpoint = true;
            options.ClaimActions.Add(new MapAllClaimsAction());
            options.ClaimActions.MapJsonKey("website", "website");
            options.ClaimActions.MapUniqueJsonKey("userNumber", "userNumber");
            options.SaveTokens = true;
            options.ClaimActions.MapAll();

            options.Events = new OpenIdConnectEvents
            {
                OnTokenValidated = async ctx => await ctx.EnsureCurrentOrganizationAsync().ConfigureAwait(false),
                OnRedirectToIdentityProvider = context =>
                {
                    if (context.Properties.Items.ContainsKey("postregisterurl"))
                    {
                        context.ProtocolMessage.SetParameter("postregisterurl",
                            context.Properties.Items["postregisterurl"]);
                    }

                    return Task.FromResult(0);
                }
            };
        });

使用 AlwaysIncludeUserClaimsInIdToken = true 应该始终包含自定义声明,我试图了解为什么会发生这种情况:(

标签: .net-coreidentityserver4

解决方案


默认情况下,.NET 会删除您拥有的大部分自定义声明。只有这些声明被传递:

options.ClaimActions.MapUniqueJsonKey("sub", "sub");
options.ClaimActions.MapUniqueJsonKey("name", "name");
options.ClaimActions.MapUniqueJsonKey("given_name", "given_name");
options.ClaimActions.MapUniqueJsonKey("family_name", "family_name");
options.ClaimActions.MapUniqueJsonKey("profile", "profile");
options.ClaimActions.MapUniqueJsonKey("email", "email");

要添加,您需要使用以下代码一一手动添加:

options.ClaimActions.MapUniqueJsonKey("website", "website");
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
options.ClaimActions.MapUniqueJsonKey("birthdate", "birthdate");

OpenIdConnectOptions.cs的源代码是有关此主题的丰富信息来源。

另一种方法是使用此方法:

options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce");

相关还使用以下方法清除声明映射:

// By default, Microsoft has some legacy claim mapping that converts
// standard JWT claims into proprietary ones. This removes those mappings.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();

推荐阅读