Reputation: 181
Recently I have migrated an API from .net core 2.2 to .net core 3.0 in order to implement an authentication using certificates.
I have followed this Microsoft documentations : https://learn.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-3.0 to do my work. But I'm facing an issue : When I call a controller Method decorated with an [Authorize] attribute, the certificate validation is never performed. If there is no certificate in the request's header, I get a 403 which is the required behavior, but If I put a certificate the supposed behavior should be the verification by the thumbprint, but nothing...
Here my certificate validation service :
public class CertificateValidationService
{
public bool ValidateCertificate(X509Certificate2 clientCertificate, string thumbprintToChek) =>
clientCertificate.Thumbprint.Equals(thumbprintToChek);
}
As said in the documentation, in StartUp.cs I have set up the following lines of code inside the ConfigureServices method
services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options => // code from ASP.NET Core sample
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices.GetService<CertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate, Configuration.GetValue<string>("Apim:CertificateThumbprint")))
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(ClaimTypes.Name, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
}
else
{
context.Fail("Invalid certificate.");
}
return Task.CompletedTask;
}
};
options.Events = new CertificateAuthenticationEvents
{
OnAuthenticationFailed = context =>
{
context.Fail("Certificate not valid.");
return Task.CompletedTask;
}
};
});
services.AddCertificateForwarding(options =>
{
options.CertificateHeader = "X-ARR-ClientCert";
options.HeaderConverter = (headerValue) =>
{
X509Certificate2 clientCertificate = null;
if (!string.IsNullOrWhiteSpace(headerValue))
{
byte[] bytes = headerValue.ToByteArray();
clientCertificate = new X509Certificate2(bytes);
}
return clientCertificate;
};
});
I have also populated the Configure method with the following code :
app.UseCertificateForwarding();
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
So when I make a request to the API, the CertificateForwarding delegation is well called, but Authentication delegation, never.
However, I have tried to implement this authentication in a brand new project (just for tests) it worked perfectly.
Upvotes: 1
Views: 2447
Reputation: 93
Your assignment to options.Events
is done twice. This way the default implementation of OnCertificateValidated
overwrites your eventhandler and your CertificateValidationService
will never be called.
Combining the two eventhandlers should give the expected result:
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
// Your implementation here.
},
OnAuthenticationFailed = context =>
{
// Your implementation here.
}
};
Upvotes: 4