How to Setup Multiple Auth Schemes in ASP.NET Core 2.0

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

// ...
}
}

Using multiple authentication schemes in ASP.NET Core 3.1?

After doing some research, I found the solution in ASP.NET core Authorization documentation in an article with the title "Authorize with a specific scheme in ASP.NET Core".

Based on the mentioned article in Microsoft ASP .NET core documentation, In some scenarios, such as Single Page Applications (SPAs), it's common to use multiple authentication methods. For example, the app may use cookie-based authentication to log in and JWT bearer authentication for JavaScript requests.

An authentication scheme is named when the authentication service is configured during authentication. For example:

public void ConfigureServices(IServiceCollection services)
{
// Code omitted for brevity

services.AddAuthentication()
.AddCookie(options => {
options.LoginPath = "/Account/Unauthorized/";
options.AccessDeniedPath = "/Account/Forbidden/";
})
.AddJwtBearer(options => {
options.Audience = "http://localhost:5001/";
options.Authority = "http://localhost:5000/";
});

In the preceding code, two authentication handlers have been added: one for cookies and one for bearer.

Selecting the scheme with the Authorize attribute

[Authorize(AuthenticationSchemes = 
JwtBearerDefaults.AuthenticationScheme)]
public class MixedController : Controller

In the preceding code, only the handler with the "Bearer" scheme runs. Any cookie-based identities are ignored.

This is the solution which solved my problem and I thought it would be good to share it with you guys for those who need this.

Asp.net core 2.0+ - Multiple Authentication Schemes (Cookie / Bearer)

I wanted to give a better explanation of this answer:

  1. I had to move services.AddAuthorization after the part were I
    added both of the schemes. This ensures both schemes are registered
    correctly.
 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

services.AddAuthentication(options => {
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options => {
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "myApi";
options.SaveTokens = true;
}).AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, options => {
options.Authority = "http://localhost:5000";
options.ApiName = "myApi";
options.RequireHttpsMetadata = false;
});

services.AddAuthorization(options => {
...
});

  1. Then instead of specifying the Authorization Scheme as a part of the
    Controller Action Authorize tag, I used a global policy when using
    services.AddAuthorization
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
CookieAuthenticationDefaults.AuthenticationScheme,
JwtBearerDefaults.AuthenticationScheme);
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});

  1. When I navigated to the any parts of the API it wouldn't redirect to the Login screen. I noticed that if you logged in first by navigating to Identity Server, then go back to that page it would actually authenticate you as normal. So I've put in what feel to be a little bit of hack. It is important that this directly goes in under the app.UseAuthentication.
  app.UseAuthentication();
app.Use(async (context, next) => {
await next();
var bearerAuth = context.Request.Headers["Authorization"]
.FirstOrDefault()?.StartsWith("Bearer ") ?? false;
if (context.Response.StatusCode == 401
&& !context.User.Identity.IsAuthenticated
&& !bearerAuth) {
await context.ChallengeAsync("oidc");
}
});

Bob's your uncle... and thanks to this post for helping considerably!! oipapio.com/question-1510997

Multiple authentication schemes in ASP.NET Core

You can target a specific authentication-scheme using an imperative approach, by calling AuthenticateAsync. Here's an example:

app2.Use(async (ctx, next) =>
{
var authenticateResult = await ctx.AuthenticateAsync("SchemeName");

if (!authenticateResult.Succeeded)
{
ctx.Response.StatusCode = 401; // e.g.
return;
}

// ...
});

AuthenticateAsync takes the authentication-scheme as an argument and returns an instance of AuthenticateResult, which indicates success or failure via Succeeded and provides the authenticated ClaimsPrincipal via Principal.

You can also perform authorisation against a specific policy using IAuthorizationService. Here's an example of how the Principal from AuthenticateResult can be passed through AuthorizeAsync:

var authorizationService = ctx.RequestServices.GetService<IAuthorizationService>();
var authorizationResult = await authorizationService.AuthorizeAsync(
authenticateResult.Principal, "PolicyName");

if (!authorizationResult.Succeeded)
{
ctx.Response.StatusCode = 403; // e.g.
return;
}

// ...

As with AuthenticateResult, AuthorizationResult indicates success or failure via Succeeded - it also provides information about why authorisation failed via Failure.

Multiple authentication methods in asp.Net core 2.2

I finally figured out how to do it. This example uses JWT authentication by default and custom authentication in certain rare cases. Please note, from what I've read, Microsoft seems to discourage writing your own auth. Please use at your own risk.

First, add this code to the startup.cs ConfigureServices method to ensure that authentication gets applied globally.

services.AddMvc(options => 
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})

Then, add this to configure the schemes you wish to use (in our case JWT and Custom).

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
// Jwt Authentication
.AddJwtBearer(options =>
{
options.Audience = ".......";
options.Authority = "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_...";
})
// Custom auth
.AddScheme<CustomAuthOptions,
CustomAuthHandler>(CustomAuthOptions.DefaultScheme, options => { });

Next create a class to hold your custom authentication options:

public class CustomAuthOptions : AuthenticationSchemeOptions
{
public const string Scheme = "custom auth";
public const string CustomAuthType = "custom auth type";
}

Finally, add an authentication handler to implement the custom authentication logic.

public class CustomAuthHandler : AuthenticationHandler<CustomAuthOptions>
{
public CustomAuthHandler(
IOptionsMonitor<CustomAuthOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
{
}

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
// Auth logic goes here
if (!Request.Headers....)
{
return Task.FromResult(AuthenticateResult.Fail("Authentication Failed."));
}

// Create authenticated user
ClaimsPrincipal principal = .... ;

List<ClaimsIdentity> identities =
new List<ClaimsIdentity> {
new ClaimsIdentity(CustomAuthOptions.CustomAuthType)};

AuthenticationTicket ticket =
new AuthenticationTicket(
new ClaimsPrincipal(identities), CustomAuthOptions.Scheme);

return Task.FromResult(AuthenticateResult.Success(ticket));
}
}

Finally, to tie it all together, add an authorize attribute to the actions you wish to use custom authorization on.

[HttpGet]
[Authorize(AuthenticationSchemes = CustomAuthOptions.Scheme)]
public HttpResponseMessage Get()
{
....
}

Now JWT authentication will automatically get applied to all actions, and custom authentication will get added to only the actions with the Authorize attribute set to the custom scheme.

I hope this helps someone.

How to support multiple authentication scheme in Web API Core 2?

try Adding your authentication service in one chain

services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer((options) =>
{
options.Authority = $"...";
options.Audience = "...";
})
.AddBasicAuthentication(credentials =>
{
Task.FromResult(credentials.username == "username" && credentials.password == "password"));
}

and also on AuthorizeAttribute you can specify which Scheme you want to authenticate the request with

[Authorize(AuthenticationSchemes = BasicAuthenticationDefaults.AuthenticationScheme + ", " + JwtBearerDefaults.AuthenticationScheme)]

ASP.NET Core Using Multiple Authentication Methods

Use policy based authentication. There you can check if current ClaimsPrincipal (context.User) has 2 Identities, 1 from each successfully passed authentication scheme. Configure policy

services.AddAuthorization(options =>
{
options.AddPolicy("RequireAllSchemes", policy =>
{
policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
policy.RequireAssertion(context =>
{
return context.User.Identities.Count() == 2;
});
});
});

Specify authorization policy for controller

[Authorize(Policy = "RequireAllSchemes")]
public class MixedController : Controller


Related Topics



Leave a reply



Submit