Reputation: 371
I am testing a self-hosted Asp Net Core Web server (Kestrel), and I am struggling with the client authentication using self-signed certificates. This is my startup code
WebApplicationBuilder webBuilder = WebApplication.CreateBuilder();
var webHostBuilder = builder.WebHost;
X509Certificate2 rootCert = new X509Certificate2(hostCertFilePath, hostCertPassword);
webHostBuilder.ConfigureKestrel(o =>
{
o.ConfigureHttpsDefaults(o =>
{
o.ServerCertificate = rootCert;
o.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
});
webHostBuilder.UseKestrel(o =>
{
o.Listen(IPAddress.Parse(myHttpsEndPointIpAddr), myHttpsEndPointPort,
listenOptions =>
{
listenOptions.UseHttps();
});
o.Listen(IPAddress.Parse(myHttpEndPointIpAddr), myHttpEndPointPort);
});
var services = webBuilder.Services;
services.AddTransient<MyCustomCertificateValidationService>();
services
.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.SelfSigned;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices
.GetService<MyCustomCertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate))
{
context.Success();
}
else
{
context.Fail("invalid cert");
}
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Fail("invalid cert");
return Task.CompletedTask;
}
};
});
...
var app = webBuilder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
This my custom certification class
public class MyCustomCertificateValidationService
{
public bool ValidateCertificate(X509Certificate2 clientCertificate)
{
// todo: check certificate thumbnail
return false;
}
}
But even if MyCustomCertificateValidationService has a method ValidateCertificate() that returns false, the controller method is still called when a client accesses the url with the route to the controller method. This is what is displayed in the log:
...
AspNetCore.Routing.EndpointRoutingMiddleware : Request matched endpoint ‘GetMyData…‘
AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler : Certificate was not authenticated. Failure message: invalid cert
AspNetCore.Routing.EndpointMiddleware : Executing endpoint ‘GetMyData…‘
...
Any clue why the controller method is still called?
Upvotes: 1
Views: 1196
Reputation: 63
You have to configure only HTTPS in appsettings.json
{
"Kestrel": {
"EndPoints": {
"Https": {
"Url": "https://<IP>:<PORT>"
}
}
}
}
Also, it is necessary to make enforcement HTTPS in Program.cs
app.UseHttpsRedirection();
Upvotes: 0
Reputation: 22495
"There is a use-case for the application that in some test environment also unauthorized calls (over http://...) should be allowed. I would prefer to use, if possible, a settings parameter to decide dynamically if http access is allowed or not, instead of "hardcode" it as [Authorize] attribute"
Of course you can do that. There is a handy way to implement your requirement using middleware for sure. Please try the code snippe below:
Http/Https Request Middleare Based On Environment:
public class CustomHttpHttpsRequestMiddleware
{
private readonly RequestDelegate next;
public CustomHttpHttpsRequestMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
//env = "Production";
if (env == "Development")
{
await next(context);
}
else
{
if (!context.Request.IsHttps)
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("HTTPS required!");
}
}
}
}
Note: In application request context
we are checking two important value, first if the request is secure means cIsHttps
and the application environment
, in Development
environment we will allow http
request. Therefore, other than, dev
or any env
based on our requirement we will reject http
request.
Register Middleware on Program.cs:
app.UseMiddleware<CustomHttpHttpsRequestMiddleware>();
Note: Make sure you have followed the correct middleware order. In order to avoid short circuiting, you could place this middleware way down of your all current middleware.
Output:
Upvotes: 0