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:
- 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 => {
...
});
- 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();
});
- 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
How to Use C# 6 with Web Site Project Type
How to Loop Over the Properties of a Class
Initializing an Array of Structs in C#
In C# What Category Does the Colon ":" Fall Into, and What Does It Really Mean
What Is Applicationexception for in .Net
Index of Currently Selected Row in Datagridview
Explicitly Freeing Memory in C#
Httpclient Single Instance with Different Authentication Headers
Iis Express Immediately Shutting-Down Running Site After Stopping Web Application
Best Way to Check for Nullable Bool in a Condition Expression (If ...)
How to Convert a File into Byte Array in Memory
Rsa Encryption, Getting Bad Length
How to Find Reason of Failed Build Without Any Error or Warning
Find Image Format Using Bitmap Object in C#