I am using Identity Server 4 and Implicit Flow and want to add some claims to the access token, the new claims or attributes are "tenantId" and "langId".
I have added langId as one of my scopes as below and then requesting that through identity server, but i get the tenantId also. How can this happen?
This the list of scopes and client configuration:
public IEnumerable<Scope> GetScopes() { return new List<Scope> { // standard OpenID Connect scopes StandardScopes.OpenId, StandardScopes.ProfileAlwaysInclude, StandardScopes.EmailAlwaysInclude, new Scope { Name="langId", Description = "Language", Type= ScopeType.Resource, Claims = new List<ScopeClaim>() { new ScopeClaim("langId", true) } }, new Scope { Name = "resourceAPIs", Description = "Resource APIs", Type= ScopeType.Resource }, new Scope { Name = "security_api", Description = "Security APIs", Type= ScopeType.Resource }, }; }
Client:
return new List<Client> { new Client { ClientName = "angular2client", ClientId = "angular2client", AccessTokenType = AccessTokenType.Jwt, AllowedGrantTypes = GrantTypes.Implicit, AllowAccessTokensViaBrowser = true, RedirectUris = new List<string>(redirectUris.Split(',')), PostLogoutRedirectUris = new List<string>(postLogoutRedirectUris.Split(',')), AllowedCorsOrigins = new List<string>(allowedCorsOrigins.Split(',')), AllowedScopes = new List<string> { "openid", "resourceAPIs", "security_api", "role", "langId" } } };
I have added the claims in the ProfileService:
public class ProfileService : IdentityServer4.Services.IProfileService { private readonly SecurityCore.ServiceContracts.IUserService _userService; public ProfileService(SecurityCore.ServiceContracts.IUserService userService) { _userService = userService; } public Task GetProfileDataAsync(ProfileDataRequestContext context) { //hardcoded them just for testing purposes List<Claim> claims = new List<Claim>() { new Claim("langId", "en"), new Claim("tenantId", "123") }; context.IssuedClaims = claims; return Task.FromResult(0); }
This is what i am requesting to get the token, the problem is i am only requesting the langId but I am getting both the tenantId and langId in the access token
http://localhost:44312/account/login?returnUrl=%2Fconnect%2Fauthorize%2Flogin%3Fresponse_type%3Did_token%2520token%26client_id%3Dangular2client%26redirect_uri%3Dhttp%253A%252F%252Flocalhost:5002%26scope%3DresourceAPIs%2520notifications_api%2520security_api%2520langId%2520navigation_api%2520openid%26nonce%3DN0.73617935552798141482424408851%26state%3D14824244088510.41368537145696305%26
Decoded access token:
{ "nbf": 1483043742, "exp": 1483047342, "iss": "http://localhost:44312", "aud": "http://localhost:44312/resources", "client_id": "angular2client", "sub": "1", "auth_time": 1483043588, "idp": "local", "langId": "en", "tenantId": "123", "scope": [ "resourceAPIs", "security_api", "langId", "openid" ], "amr": [ "pwd" ] }
Claims in access tokens. JWTs are split into three pieces: Header - Provides information about how to validate the token including information about the type of token and how it was signed. Payload - Contains all of the important data about the user or application that's attempting to call the service.
Claims are pieces of information about a user that have been packaged, signed into security tokens and sent by an issuer or identity provider to relying party applications through a security token service (STS).
This answer was written for Identityserver4 on .Net core 2 to use it for .Net core 3, this answer may help you, but you need to test and change a few things.
This is my sample code, works well and JWT contains all roles and claims
You can see how to implement Identityserver4 with ASP.Net core identity here http://docs.identityserver.io/en/release/quickstarts/6_aspnet_identity.html https://github.com/IdentityServer/IdentityServer4.Samples/tree/dev/Quickstarts/6_AspNetIdentity
1- identity server startup.cs
public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.AddMvc(); services.AddTransient<IEmailSender, AuthMessageSender>(); services.AddTransient<ISmsSender, AuthMessageSender>(); //Add IdentityServer services //var certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "LocalhostCert.pfx"), "123456"); services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryIdentityResources(Configs.IdentityServerConfig.GetIdentityResources()) .AddInMemoryApiResources(Configs.IdentityServerConfig.GetApiResources()) .AddInMemoryClients(Configs.IdentityServerConfig.GetClients()) .AddAspNetIdentity<ApplicationUser>() .AddProfileService<Configs.IdentityProfileService>(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); //app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseIdentity(); // Adds IdentityServer app.UseIdentityServer(); // Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715 app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Account}/{action=Login}/{id?}"); }); }
2- IdentityServerConfig.cs
using IdentityServer4; using IdentityServer4.Models; using System.Collections.Generic; namespace IdentityAuthority.Configs { public class IdentityServerConfig { // scopes define the resources in your system public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() }; } // scopes define the API resources public static IEnumerable<ApiResource> GetApiResources() { //Create api resource list List<ApiResource> apiResources = new List<ApiResource>(); //Add Application Api API resource ApiResource applicationApi = new ApiResource("ApplicationApi", "Application Api"); applicationApi.Description = "Application Api resource."; apiResources.Add(applicationApi); //Add Application Api API resource ApiResource definitionApi = new ApiResource("DefinitionApi", "Definition Api"); definitionApi.Description = "Definition Api."; apiResources.Add(definitionApi); //Add FF API resource ApiResource ffApi = new ApiResource("FFAPI", "Fule .netfx API"); ffApi.Description = "Test using .net 4.5 API application with IdentityServer3.AccessTokenValidation"; apiResources.Add(ffApi); return apiResources; } // client want to access resources (aka scopes) public static IEnumerable<Client> GetClients() { //Create clients list like webui, console applications and... List<Client> clients = new List<Client>(); //Add WebUI client Client webUi = new Client(); webUi.ClientId = "U2EQlBHfcbuxUo"; webUi.ClientSecrets.Add(new Secret("TbXuRy7SSF5wzH".Sha256())); webUi.ClientName = "WebUI"; webUi.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials; webUi.RequireConsent = false; webUi.AllowOfflineAccess = true; webUi.AlwaysSendClientClaims = true; webUi.AlwaysIncludeUserClaimsInIdToken = true; webUi.AllowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId); webUi.AllowedScopes.Add(IdentityServerConstants.StandardScopes.Profile); webUi.AllowedScopes.Add("ApplicationApi"); webUi.AllowedScopes.Add("DefinitionApi"); webUi.AllowedScopes.Add("FFAPI"); webUi.ClientUri = "http://localhost:5003"; webUi.RedirectUris.Add("http://localhost:5003/signin-oidc"); webUi.PostLogoutRedirectUris.Add("http://localhost:5003/signout-callback-oidc"); clients.Add(webUi); //Add IIS test client Client iisClient = new Client(); iisClient.ClientId = "b8zIsVfAl5hqZ3"; iisClient.ClientSecrets.Add(new Secret("J0MchGJC8RzY7J".Sha256())); iisClient.ClientName = "IisClient"; iisClient.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials; iisClient.RequireConsent = false; iisClient.AllowOfflineAccess = true; iisClient.AlwaysSendClientClaims = true; iisClient.AlwaysIncludeUserClaimsInIdToken = true; iisClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId); iisClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.Profile); iisClient.AllowedScopes.Add("ApplicationApi"); iisClient.AllowedScopes.Add("DefinitionApi"); iisClient.AllowedScopes.Add("FFAPI"); iisClient.ClientUri = "http://localhost:8080"; iisClient.RedirectUris.Add("http://localhost:8080/signin-oidc"); iisClient.PostLogoutRedirectUris.Add("http://localhost:8080/signout-callback-oidc"); clients.Add(iisClient); return clients; } } }
3 - IdentityProfileService.cs
using IdentityServer4.Services; using System; using System.Threading.Tasks; using IdentityServer4.Models; using IdentityAuthority.Models; using Microsoft.AspNetCore.Identity; using IdentityServer4.Extensions; using System.Linq; namespace IdentityAuthority.Configs { public class IdentityProfileService : IProfileService { private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory; private readonly UserManager<ApplicationUser> _userManager; public IdentityProfileService(IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, UserManager<ApplicationUser> userManager) { _claimsFactory = claimsFactory; _userManager = userManager; } public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var sub = context.Subject.GetSubjectId(); var user = await _userManager.FindByIdAsync(sub); if (user == null) { throw new ArgumentException(""); } var principal = await _claimsFactory.CreateAsync(user); var claims = principal.Claims.ToList(); //Add more claims like this //claims.Add(new System.Security.Claims.Claim("MyProfileID", user.Id)); context.IssuedClaims = claims; } public async Task IsActiveAsync(IsActiveContext context) { var sub = context.Subject.GetSubjectId(); var user = await _userManager.FindByIdAsync(sub); context.IsActive = user != null; } } }
4 - In my client mvc core project I added 3 nuget packages
.Microsoft.AspNetCore.Authentication.Cookies
.Microsoft.AspNetCore.Authentication.OpenIdConnect
.IdentityModel
5- This is my startup.cs in my client mvc core project
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); //app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); //Setup OpenId and Identity server app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationScheme = "Cookies", AutomaticAuthenticate = true }); app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { Authority = "http://localhost:5000", ClientId = "U2EQlBHfcbuxUo", ClientSecret = "TbXuRy7SSF5wzH", AuthenticationScheme = "oidc", SignInScheme = "Cookies", SaveTokens = true, RequireHttpsMetadata = false, GetClaimsFromUserInfoEndpoint = true, ResponseType = "code id_token", Scope = { "ApplicationApi", "DefinitionApi", "FFAPI", "openid", "profile", "offline_access" }, TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" } }); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
6 - In my API I added this nuget package
.IdentityServer4.AccessTokenValidatio
and my startup.cs is like this
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); //IdentityServer4.AccessTokenValidation app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions { Authority = "http://localhost:5000", RequireHttpsMetadata = false, ApiName = "ApplicationApi" }); app.UseMvc(); }
Now I can use [Authorize(Role="SuperAdmin, Admin")] in both client web app and API app.
User.IsInRole("Admin")
also I have access to claims
HttpContext.User.Claims var q = (from p in HttpContext.User.Claims where p.Type == "role" select p.Value).ToList(); var q2 = (from p in HttpContext.User.Claims where p.Type == "sub" select p.Value).First();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With