Certificate was not authenticated, request succeeds anyway with asp.net 5 on Azure App Service

Viewed 408

I'm trying to enable client certificate authentication for my server api per here:

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-5.0

The problem I'm seeing is that the certificate is sent by the client (as required by the Azure App Service settings), but even though I deliberately call context.Fail, the request is always processed and returns 200. I guess I'm probably missing something sort of fundamental - I'm totally new to, well, pretty much all of this server-side .NET. Thanks for looking!

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).
            AddCertificate(options =>
            {
                options.AllowedCertificateTypes = CertificateTypes.All;
                options.Events = new CertificateAuthenticationEvents
                {
                    OnCertificateValidated = context => {
                        context.Fail("FAIL!!!");
                        _logger.LogWarning("OnCertificateValidated!!!");
                        return Task.CompletedTask; },
                    OnAuthenticationFailed = context => {
                        context.Fail("BAD cert.  BAD!");
                        _logger.LogWarning("OnAuthenticationFailed!!!");
                        return Task.CompletedTask; }
                };
            }).
            AddCertificateCache();


        services.AddLogging(loggingBuilder =>
        {
            loggingBuilder.AddConsole();
            loggingBuilder.AddDebug();
            loggingBuilder.AddAzureWebAppDiagnostics();
        });

        services.AddControllers();
    }

And Configure

        private static ILogger<Startup> _logger;

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
    {
        _logger = logger;

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        
        app.UseRouting();

        app.UseCertificateForwarding();
        app.UseAuthentication();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });

    }

In my Azure App Service log stream I see

2021-04-21 05:42:07.759 +00:00 [Warning] MyApi.Startup: OnCertificateValidated!!!!!
2021-04-21 05:42:07.759 +00:00 [Information] Microsoft.AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler: Certificate was not authenticated. Failure message: FAIL!!!

and if I configure App Service to allow no certificate, I get a different log, but the request still

2021-04-21 05:27:12.119 +00:00 [Debug] Microsoft.AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler: No client certificate found.
2021-04-21 05:27:12.120 +00:00 [Debug] Microsoft.AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler: AuthenticationScheme: Certificate was not authenticated.

But, in all cases, the request succeeds, while the above linked documentation seemed to indicate I should see a 403 (Forbidden) result -- which I did when I sent no certificate and Azure configuration was set to require a certificate. That's the only time I can get it to fail.

I see that I can perhaps use a method as described here -- retrieve the request header and parse and validate it entirely myself. But isn't the above supposed to work? https://docs.microsoft.com/en-us/azure/app-service/app-service-web-configure-tls-mutual-auth

1 Answers

I ran into this as well. My issue was that I had not declared any controller routes as requiring Authentication.

Two possible fixes depending on your use case:

Turn on auth for all routes

// Require auth by default for all routes
services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

Turn on auth for specific controllers/actions with an attribute per the docs

[Authorize]
public class AccountController : Controller
{
   public ActionResult Login()
   {
   }

   public ActionResult Logout()
   {
   }
}
Related