Jeff Putz Jeff Putz - 1 year ago 230
ASP.NET (C#) Question

How do I setup multiple auth schemes in ASP.NET Core 2.0?

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?}");
});
}
}
}

Answer Source

Navigating these changes has been difficult, but I'm guessing that I'm doing .AddScheme wrong.

Don't use the AddScheme: it's a low-level method designed for handlers writers.

How do I setup multiple auth schemes in ASP.NET Core 2.0?

To register the cookies 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's important to note that you can't register multiple default schemes like you could in 1.x (the whole point of this huge refactoring is to avoid having multiple automatic authentication middleware at the same time).

If you absolutely need to emulate this behavior in 2.0, you can write a custom middleware that manually calls AuthenticateAsync() and creates a ClaimsPrincipal containing all the identities 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();
        });

        // ...
    }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download