I'm trying to migrate my auth stuff to Core 2.0 and having an issue using my own authentication scheme. My service setup in startup looks like this:
var authenticationBuilder = services.AddAuthentication(options => { options.AddScheme("myauth", builder => { builder.HandlerType = typeof(CookieAuthenticationHandler); }); }) .AddCookie();
My login code in the controller looks like this:
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'm in a controller or action filter, I only have one identity, and it's not an authenticated one:
var identity = context.HttpContext.User.Identities.SingleOrDefault(x => x.AuthenticationType == "myauth");
Navigating these changes has been difficult, but I'm guessing that I'm doing .AddScheme wrong. Any suggestions?
EDIT: Here's (essentially) a clean app that results not in two sets of Identities on 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 Startup:
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?}"); }); } } }
This blog starts with authentication and authorization concepts and after that explains the three default important ways and three custom authentication ways for doing authentication and authorization i.e. windows, forms ,passport, multipass, JWT and SAML authentication.
Authentication is the process of determining a user's identity. Authorization is the process of determining whether a user has access to a resource. In ASP.NET Core, authentication is handled by the authentication service, IAuthenticationService, which is used by authentication middleware.
The 4 main schemes of REST API authentication are- Basic Authentication. Token Based Authentication. API Key Based Authentication. OAuth (Open Authorization)
The windows Authentication provider lets you authenticates users based on their windows accounts. This provider uses IIS to perform the authentication and then passes the authenticated identity to your code. This is the default provided for ASP.net.
Edit of December 2019: please consider this answer before anything else: Use multiple JWT Bearer Authentication
My old answer (that does not fit using multiple JWT but only JWT + API key, as a user commented):
Another possibility is to determine at runtime which authentication policy scheme to choose, I had the case where I could have an http authentication bearer token header or a cookie.
So, thanks to https://github.com/aspnet/Security/issues/1469
JWT token if any in request header, then OpenIdConnect (Azure AD) or anything 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; }); ... }
Edit of 07/2019: I must add a link to the following proposal, because it's very helpful too: you may not use parameters in AddAuthentication()
as I did, because this would setup a default scheme. Everything is well explained here: Use multiple JWT Bearer Authentication. I really like this other approach!
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