IdentityServer / IdentityServer4

OpenID Connect and OAuth 2.0 Framework for ASP.NET Core
https://identityserver.io
Apache License 2.0
9.21k stars 4k forks source link

Identity Server 4 - Custom Identity Claims are not showing in Id Token, but scopes are visible in Access Token. #5479

Closed vamsivallabhaneni111 closed 1 year ago

vamsivallabhaneni111 commented 1 year ago

Requirement: Here I have MVC Client request IdentityServer for Id & access token using Auth-Code mechanism.

Infra: .NetCore = 3.1 & IdentityServer4 = 4.1.2 & IdentityServer4.AspNetIdentity=4.1.2

For this I have used 2 types of configurations.

Type1 - AlwaysIncludeUserClaimsInIdToken = true Type2 - AlwaysIncludeUserClaimsInIdToken = false & options.ClaimActions.MapJsonKey() to map the claims. & GetClaimsFromUserInfoEndpoint = true When you see the code below, I have seggregated as 3 layers, first one is Type1 & sceond one is Type2 and common Config is for both Type1 & Type2(as it doesn't vary).

Common Configuration.

Identity Server Proj::

Program.cs //Add Claims when user is created.

public static void Main(string[] args)
        {
            //DI is ready when we call create host builder.
            var host = CreateHostBuilder(args).Build();

            // use DI to inject/seeding of new users.
            using (var scope = host.Services.CreateScope())
            {
               var userManager = scope.ServiceProvider
                    .GetRequiredService<UserManager<IdentityUser>>();
                userManager.CreateAsync(SeedingUsers.GetUser, "password").GetAwaiter().GetResult();
                userManager.AddClaimsAsync(SeedingUsers.GetUser, new List<Claim>
                {
                    new Claim("employee_id", "employee_id") //added employee_id as custom claim
                }).GetAwaiter().GetResult();
            }

            host.Run();
        }

startup.cs

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<AppDbContext>(config =>
            {
                config.UseInMemoryDatabase("Memory");
            });

            services.AddIdentity<IdentityUser, IdentityRole>(config =>
            {
                config.Password.RequiredLength = 4;
                config.Password.RequireDigit = false;
                config.Password.RequireUppercase = false;
                config.Password.RequireNonAlphanumeric = false;
            })
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

            services.ConfigureApplicationCookie(config =>
            {
                config.Cookie.Name = "Identity_Cookie"; //It holds logged-in user's data.
                config.LoginPath = "/Accounts/Login";
            });

            services.AddIdentityServer()
                .AddAspNetIdentity<IdentityUser>()

                .AddInMemoryIdentityResources(MyConfiguration.GetIdentityResources)
                .AddInMemoryApiResources(MyConfiguration.GetApiResources)
                .AddInMemoryClients(MyConfiguration.GetClients)
                .AddDeveloperSigningCredential();

            services.AddControllersWithViews();
        }

1. Type-1 Configuration.

Identity Server Proj::

MyConfiguration.cs //used to configure Identity.

public static class MyConfiguration
    {
        public static IEnumerable<Client> GetClients =>
            new List<Client>
            {
                // Registering client for mvc
                new Client
                {
                    ClientId = "client_id_mvc",
                    ClientSecrets = new List<Secret>()
                    {
                        new Secret("client_secret_mvc".ToSha256())
                    },
                    RedirectUris = { "https://localhost:44362/signin-oidc" },
                    AllowedGrantTypes = GrantTypes.Code,
                    AllowedScopes = { 
                        "Aum", 
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "custom.employee_profile", //custom Id-Scope
                    },
                    AlwaysIncludeUserClaimsInIdToken = true,
                    RequireConsent = false,
                }
            };

        // This is used to craft the Id Token by IdentityServer.
        public static IEnumerable<IdentityResource> GetIdentityResources =>
            new List<IdentityResource>
            {
                new IdentityResources.OpenId(),

                new IdentityResources.Profile(),

                new IdentityResource
                {
                    Name = "custom.employee_profile",
                    UserClaims = {"employee_id"},
                }
            };

        // This is used to craft the access Token by IdentityServer.
        public static IEnumerable<ApiResource> GetApiResources =>
            new List<ApiResource> {

                new ApiResource("Aum")
            };
    }

MVC Client Proj::

Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(config =>
            {
                config.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                config.DefaultChallengeScheme = "oidc";
                config.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddOpenIdConnect("oidc", options =>
            {
                options.Authority = "https://localhost:44322/";
                options.ClientId = "client_id_mvc";
                options.ClientSecret = "client_secret_mvc";
                options.SaveTokens = true;
                options.ResponseType = "code";

                options.GetClaimsFromUserInfoEndpoint = false; // process -1

                //custom claims
                options.Scope.Clear();
                options.Scope.Add("openid");
                options.Scope.Add("custom.employee_profile"); //Requesting for custom scope. 
            });

        services.AddControllersWithViews();
    }

2. Type-2 Configuration.

Identity Server Proj::

MyConfiguration.cs //used to configure Identity.

public static class MyConfiguration
    {
        public static IEnumerable<Client> GetClients =>
                // Registering client for mvc
                new Client
                {
                    ClientId = "client_id_mvc",
                    ClientSecrets = new List<Secret>()
                    {
                        new Secret("client_secret_mvc".ToSha256())
                    },
                    RedirectUris = { "https://localhost:44362/signin-oidc" },
                    AllowedGrantTypes = GrantTypes.Code,
                    AllowedScopes = { 
                        "Aum", 
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "custom.employee_profile", //custom Id-Scope
                    },

                    AlwaysIncludeUserClaimsInIdToken = false,  // process -2
                    RequireConsent = false,
                }
            };

        // This is used to craft the Id Token by IdentityServer.
        public static IEnumerable<IdentityResource> GetIdentityResources =>
            new List<IdentityResource>
            {
                new IdentityResources.OpenId(),

                new IdentityResources.Profile(),

                new IdentityResource
                {
                    Name = "custom.employee_profile",
                    UserClaims = {"employee_id"},
                },
            };

        // This is used to craft the access Token by IdentityServer.
        public static IEnumerable<ApiResource> GetApiResources =>
            new List<ApiResource> {
                new ApiResource("Aum")
            };

}

MVC Client Proj::

Startup.cs

public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(config =>
                {
                    config.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    config.DefaultChallengeScheme = "oidc";
                    config.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                })
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddOpenIdConnect("oidc", options =>
                {
                    options.Authority = "https://localhost:44322/";
                    options.ClientId = "client_id_mvc";
                    options.ClientSecret = "client_secret_mvc";
                    options.SaveTokens = true;
                    options.ResponseType = "code";

                    //used for mapping custom clian in Identity to Client.
                    options.ClaimActions.DeleteClaim("amr");
                    options.ClaimActions.DeleteClaim("s_hash");
                    // process -2 This will map Identity claims to current mvc client client.  
                    options.ClaimActions.MapJsonKey("employee_code", "employee_id"); 

              //used for round trips to the Identity, This will hit /userInfo endpoint to get Id token. 
                    options.GetClaimsFromUserInfoEndpoint = true;  // process -2

                    //custom claims
                    options.Scope.Clear();
                    options.Scope.Add("openid");
                    options.Scope.Add("profile");
                    options.Scope.Add("custom.employee_profile");
                });

            services.AddControllersWithViews();
        }

Expected result: Id_token with scope employee_id claim (scope of custom.employee_profile).

Actual: Missing employee_id claim in Id_token

image image
leastprivilege commented 1 year ago

Important update

This organization is not maintained anymore besides critical security bugfixes (if feasible). This organization will be archived when .NET Core 3.1 end of support is reached (3rd Dec 2022). All new development is happening in the new Duende Software organization.

The new Duende IdentityServer comes with a commercial license but is free for dev/testing/personal projects and companies or individuals making less than 1M USD gross annnual revenue. Please get in touch with us if you have any question.

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Questions are community supported only and the authors/maintainers may or may not have time to reply. If you or your company would like commercial support, please see here for more information.

github-actions[bot] commented 1 year ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.