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

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();
        });

        // ...
    }
}

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!


https://stackoverflow.com/a/51897159/4425154's solution helps. Couple of items to consider on top the solution mentioned,

  1. Make sure you are using .net core run-time 2.1 or above
  2. Make sure you an authorization policy as mentioned below 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));
        })