1

I am running IdentityServer4 hosted in an MVC application similar to https://github.com/IdentityServer/IdentityServer4/tree/master/samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity.

This IdentityServer host exposes a ProfileService like such near the bottom of the ConfigureServices method.

services.AddTransient<IProfileService, ProfileService>();

From all of the examples and QuickStarts I have looked at, I am not seeing where the IdentityServer MVC host itself can access profile data. Meaning the IDServ MVC host is a client of itself and can access claim data. I have seen examples where IDServ adds OpenIdConnect as an external provider but it seems off that the MVC app would list itself as the external provider so that I can get ProfileService claim data.

My Startup.cs (of the Host IDServ MVC App) looks like this

public class Startup { public Startup(IConfiguration configuration, IHostingEnvironment env) { Configuration = configuration; HostingEnvironment = env; } ... removed for brevity public void ConfigureServices(IServiceCollection services) { var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name; var connectionString = Configuration.GetConnectionString("connString"); services.AddControllersWithViews(); // configures IIS out-of-proc settings (see https://github.com/aspnet/AspNetCore/issues/14882) services.Configure<IISOptions>(iis => { iis.AuthenticationDisplayName = "Windows"; iis.AutomaticAuthentication = false; }); // configures IIS in-proc settings services.Configure<IISServerOptions>(iis => { iis.AuthenticationDisplayName = "Windows"; iis.AutomaticAuthentication = false; }); services.AddDbContext<AuthDbContext>(b => b.UseSqlServer(connectionString, sqlOptions => { sqlOptions.MigrationsAssembly(typeof(AuthDbContext).GetTypeInfo().Assembly.GetName().Name); sqlOptions.EnableRetryOnFailure(5, TimeSpan.FromSeconds(1), null); }) ); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<AuthDbContext>() .AddDefaultTokenProviders(); services.AddIdentityServer(options => { options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; }) .AddConfigurationStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); }) .AddOperationalStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); options.EnableTokenCleanup = true; }) .AddAspNetIdentity<ApplicationUser>() .AddSigningAuthority(HostingEnvironment, Configuration) .AddProfileService<ProfileService>(); services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies"); services.AddTransient<IProfileService, ProfileService>(); services.AddTransient<AzureTableStorageLoggerMiddleware>(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory, IHttpContextAccessor accessor) { app.UseMiddleware<AzureTableStorageLoggerMiddleware>(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseIdentityServer(); app.UseAuthorization(); loggerFactory.AddTableStorage(env.EnvironmentName + "Auth", Configuration["AzureStorageConnectionString"], accessor); app.UseMiddleware<AzureTableStorageLoggerMiddleware>(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } } 

As you can see above, I am not using .AddOpenIdConnect on itself and am wondering whether I need to add it on the host itself so that I can obtain the profile service claim data on the host IDServ app like such...

services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.SignInScheme = "Cookies"; options.Authority = "https://localhost:44378/"; //seems silly to have it point to it's own host options.RequireHttpsMetadata = false; options.GetClaimsFromUserInfoEndpoint = true; options.ClientId = "idserv"; options.ClientSecret = "<<>>"; options.ResponseType = "code id_token token"; options.SaveTokens = true; }); 

On the bright side, a completely separate MVC Client does obtain the ProfileService claim data when using the .AddOpenIdConnect() middleware approach, just not the host.

Thanks

1 Answer 1

2
  1. As IdentityServer's documentation said:

You can provide a callback to transform the claims of the incoming token after validation. Either use the helper method, e.g.:

 services.AddLocalApiAuthentication(principal => { principal.Identities.First().AddClaim(new Claim("additional_claim", "additional_value")); return Task.FromResult(principal); }); 

you can read complete guide in Claims Transformation

  1. You can write a new MiddleWare and load user claims.
 public class ClaimsMiddleware { private readonly RequestDelegate _next; public ClaimsMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext httpContext, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager) { if (httpContext.User != null && httpContext.User.Identity.IsAuthenticated) { var sub = httpContext.User.Claims.SingleOrDefault(c => c.Type == JwtClaimTypes.Subject); if (sub != null) { var user = await userManager.FindByIdAsync(sub.Value); if (user != null) { var claims = //fill this variable in your way; var appIdentity = new ClaimsIdentity(claims); httpContext.User.AddIdentity(appIdentity); } } await _next(httpContext); } } } 

and call it in your Startup.cs

 app.UseIdentityServer(); app.UseAuthorization(); app.UseMiddleware<ClaimsMiddleware>(); 
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. Is there no way to accomplish the same outcome solely in the IDServ host Startup.cs file?
Yes, there is, you can add your calims in the Startup.cs file when you are adding localApiAuthentication services.AddLocalApiAuthentication. you can find complete guide in Claims Transformation.
The middleware approach works better for me so that I can access the userManager via the DI. Thank you Mehrdad

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.