In ASP.NET Core 1.x I could use authentication methods in Configure but now in ASP.NET Core 2.0 I have to set everything in ConfigureServices and can't configure it in Configure method. For example
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication()
.AddCookie()
.AddXX();
}
and then in
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
....
app.UseAuthentication();
}
in the past, I could use something like
app.UseOpenIdConnectAuthentication();
and I can't configure it anymore like this.
so how I can use something like this now in ASP.NET Core 2.0?
app.Map(new PathString("/MyPath"), i => i.UseMyAuthMethod());
The Microsoft docs say what to do if you want to use multiple authentication schemes in ASP.NET Core 2+:
The following example enables dynamic selection of schemes on a per request basis. That is, how to mix cookies and API authentication:
public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { // For example, can foward any requests that start with /api // to the api scheme. options.ForwardDefaultSelector = ctx => ctx.Request.Path.StartsWithSegments("/api") ? "Api" : null; }) .AddYourApiAuth("Api"); }
I had to implement a mixed-authentication solution in which I needed Cookie authentication for some requests and Token authentication for other requests. Here is what it looks like for me:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
// if URL path starts with "/api" then use Bearer authentication instead
options.ForwardDefaultSelector = httpContext => httpContext.Request.Path.StartsWithSegments("/api") ? JwtBearerDefaults.AuthenticationScheme : null;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o =>
{
o.TokenValidationParameters.ValidateIssuerSigningKey = true;
o.TokenValidationParameters.IssuerSigningKey = symmetricKey;
o.TokenValidationParameters.ValidAudience = JwtSignInHandler.TokenAudience;
o.TokenValidationParameters.ValidIssuer = JwtSignInHandler.TokenIssuer;
});
where the JWT Bearer authentication is implemented as described in this answer.
One of the biggest 'gotchas' for me was this: Even though the Cookies
Policy forwards requests with URLs that start with "/api" to the Bearer
policy, the cookie-authenticated users can still access those URLs if you're using the [Authorize]
annotation. If you want those URLs to only be accessed through Bearer
authentication, you must use the [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
annotation on the API Controllers/Actions.
In 2.0, the best option to do per-route authentication is to use a custom IAuthenticationSchemeProvider
:
public class CustomAuthenticationSchemeProvider : AuthenticationSchemeProvider
{
private readonly IHttpContextAccessor httpContextAccessor;
public CustomAuthenticationSchemeProvider(
IHttpContextAccessor httpContextAccessor,
IOptions<AuthenticationOptions> options)
: base(options)
{
this.httpContextAccessor = httpContextAccessor;
}
private async Task<AuthenticationScheme> GetRequestSchemeAsync()
{
var request = httpContextAccessor.HttpContext?.Request;
if (request == null)
{
throw new ArgumentNullException("The HTTP request cannot be retrieved.");
}
// For API requests, use authentication tokens.
if (request.Path.StartsWithSegments("/api"))
{
return await GetSchemeAsync(OAuthValidationDefaults.AuthenticationScheme);
}
// For the other requests, return null to let the base methods
// decide what's the best scheme based on the default schemes
// configured in the global authentication options.
return null;
}
public override async Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync() =>
await GetRequestSchemeAsync() ??
await base.GetDefaultAuthenticateSchemeAsync();
public override async Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync() =>
await GetRequestSchemeAsync() ??
await base.GetDefaultChallengeSchemeAsync();
public override async Task<AuthenticationScheme> GetDefaultForbidSchemeAsync() =>
await GetRequestSchemeAsync() ??
await base.GetDefaultForbidSchemeAsync();
public override async Task<AuthenticationScheme> GetDefaultSignInSchemeAsync() =>
await GetRequestSchemeAsync() ??
await base.GetDefaultSignInSchemeAsync();
public override async Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync() =>
await GetRequestSchemeAsync() ??
await base.GetDefaultSignOutSchemeAsync();
}
Don't forget to register it in the DI container (ideally, as a singleton):
// IHttpContextAccessor is not registered by default
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IAuthenticationSchemeProvider, CustomAuthenticationSchemeProvider>();
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