Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Identity Server 4: adding claims to access token

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"   ] } 
like image 317
Hussein Salman Avatar asked Dec 29 '16 20:12

Hussein Salman


People also ask

What is claim in access token?

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.

What are claims in Identity Server?

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).


Video Answer


1 Answers

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.


I am using asp.net Identity and Entity Framework with Identityserver4.

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(); 
like image 89
Mohammad Karimi Avatar answered Sep 20 '22 22:09

Mohammad Karimi