How to configure multiple authentication schemes in ASP.NET Core 2.0?

I am trying to port my auth file to Core 2.0 and am having a problem using my own authentication scheme. My service setup at startup is as follows:

var authenticationBuilder = services.AddAuthentication(options => { options.AddScheme("myauth", builder => { builder.HandlerType = typeof(CookieAuthenticationHandler); }); }) .AddCookie(); 

My controller login code is as follows:

 var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Name) }; var props = new AuthenticationProperties { IsPersistent = persistCookie, ExpiresUtc = DateTime.UtcNow.AddYears(1) }; var id = new ClaimsIdentity(claims); await HttpContext.SignInAsync("myauth", new ClaimsPrincipal(id), props); 

But when I am in the controller or action filter, I have only one identity, and it is not authenticated:

 var identity = context.HttpContext.User.Identities.SingleOrDefault(x => x.AuthenticationType == "myauth"); 

Navigating these changes was difficult, but I assume that I am doing .AddScheme incorrectly. Any suggestions?

EDIT: Here (essentially) is a clean application that outputs not two sets of identifiers in User.Identies:

 namespace WebApplication1.Controllers { public class Testy : Controller { public IActionResult Index() { var i = HttpContext.User.Identities; return Content("index"); } public async Task<IActionResult> In1() { var claims = new List<Claim> { new Claim(ClaimTypes.Name, "In1 name") }; var props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddYears(1) }; var id = new ClaimsIdentity(claims); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id), props); return Content("In1"); } public async Task<IActionResult> In2() { var claims = new List<Claim> { new Claim(ClaimTypes.Name, "a2 name") }; var props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddYears(1) }; var id = new ClaimsIdentity(claims); await HttpContext.SignInAsync("a2", new ClaimsPrincipal(id), props); return Content("In2"); } public async Task<IActionResult> Out1() { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); return Content("Out1"); } public async Task<IActionResult> Out2() { await HttpContext.SignOutAsync("a2"); return Content("Out2"); } } } 

And launch:

 namespace WebApplication1 { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie("a2"); services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } } 
+18
source share
4 answers

Navigating these changes was difficult, but I assume that I am doing .AddScheme incorrectly.

Do not use AddScheme : this is a low-level method intended for authors of handlers.

How to configure multiple authentication schemes in ASP.NET Core 2.0?

To register a cookie handler, simply do:

 public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(options => { options.DefaultScheme = "myauth1"; }) .AddCookie("myauth1"); .AddCookie("myauth2"); } public void Configure(IApplicationBuilder app) { app.UseAuthentication(); // ... } } 

It is important to note that you cannot register several default schemes, for example, in 1.x (all this huge reorganization is to avoid the simultaneous use of several intermediate automatic authentication programs).

If you absolutely must emulate this behavior in version 2.0, you can write your own middleware that manually calls AuthenticateAsync() and creates a ClaimsPrincipal containing all the identifiers you need:

 public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(options => { options.DefaultScheme = "myauth1"; }) .AddCookie("myauth1"); .AddCookie("myauth2"); } public void Configure(IApplicationBuilder app) { app.UseAuthentication(); app.Use(async (context, next) => { var principal = new ClaimsPrincipal(); var result1 = await context.AuthenticateAsync("myauth1"); if (result1?.Principal != null) { principal.AddIdentities(result1.Principal.Identities); } var result2 = await context.AuthenticateAsync("myauth2"); if (result2?.Principal != null) { principal.AddIdentities(result2.Principal.Identities); } context.User = principal; await next(); }); // ... } } 
+19
source

Another possibility is to determine at run time which authentication policy scheme to choose, I had a case where I might have an HTTP authentication media token header or cookie.

So thanks https://github.com/aspnet/Security/issues/1469

JWT token, if it is in the request header, then OpenIdConnect (Azure AD) or something else.

 public void ConfigureServices(IServiceCollection services) { // Add CORS services.AddCors(); // Add authentication before adding MVC // Add JWT and Azure AD (that uses OpenIdConnect) and cookies. // Use a smart policy scheme to choose the correct authentication scheme at runtime services .AddAuthentication(sharedOptions => { sharedOptions.DefaultScheme = "smart"; sharedOptions.DefaultChallengeScheme = "smart"; }) .AddPolicyScheme("smart", "Authorization Bearer or OIDC", options => { options.ForwardDefaultSelector = context => { var authHeader = context.Request.Headers["Authorization"].FirstOrDefault(); if (authHeader?.StartsWith("Bearer ") == true) { return JwtBearerDefaults.AuthenticationScheme; } return OpenIdConnectDefaults.AuthenticationScheme; }; }) .AddJwtBearer(o => { o.Authority = Configuration["JWT:Authentication:Authority"]; o.Audience = Configuration["JWT:Authentication:ClientId"]; o.SaveToken = true; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) .AddAzureAd(options => Configuration.Bind("AzureAd", options)); services .AddMvc(config => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); // Authentication is required by default config.Filters.Add(new AuthorizeFilter(policy)); config.RespectBrowserAcceptHeader = true; }); ... } 

Editing from 07/2019: I have to add a link to the following sentence because it is also very useful: you cannot use the parameters in AddAuthentication() like me, because this will set the default scheme. Everything is well explained here: use multiple authentication on JWT media. I really like this other apparoach!

+25
source

fooobar.com/questions/1270892 / ... helps the solution. A couple of issues that should be considered in addition to the solution mentioned,

  1. Make sure you are using .net core run-time 2.1 or higher
  2. Make sure you use the authorization policy as follows if you are using middleware

      services.AddMvc(options => { var defaultPolicy = new AuthorizationPolicyBuilder(new[] { CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme }) .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(defaultPolicy)); }) 
+3
source

In case someone needs a solution, here is what I did:

 services.AddMvc(options => { var defaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes(IdentityServerAuthenticationDefaults.AuthenticationScheme, BasicAuthenticationDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(defaultPolicy)); }); services.AddAuthentication() .AddIdentityServerAuthentication(option config here) .AddBasicAuthentication(setting); 
0
source

Source: https://habr.com/ru/post/1270892/


All Articles