Search code examples
c#httpauthenticationx509certificate2flurl

C# Flurl - Add WebRequestHandler to FlurlClient


I am working with Flurl to hit an API that requires certificate-based authentication. I have seen from this SO post that adding a certificate to a WebRequestHandler and instructing an HttpClient to use this handler is easy.

However, it is not so clear for me using Flurl. I have tried the follwing three things.

Extending DefaultHttpFactory

I first suspected that I needed to create my own X509HttpFactory : DefaultHttpFactory which would create the handler and assign it to the HttpClient. However, in viewing the source, I notice that the constructor for CreateClient already expects a handler. Where does this handler come from?

Creating Client using DefaultHttpFactory

WebRequestHandler handler = new WebRequestHandler();
handler.ClientCertificates.Add(myX509Cert);
var clientFactory = new DefaultHttpClientFactory();
FlurlClient fc = clientFactory.CreateClient(url, handler);

This does not compile as HttpClient cannot be casted to FlurlClient

Use ConfigureHttpClient

var clientFactory = new DefaultHttpClientFactory();
FlurlClient fc = new Url("foobar.com").ConfigureHttpClient(client => client = clientFactory
  .CreateClient(url, handler));

This seems like the most viable option, but I'm unsure since the delegate is an Action with no return type.

The Question

What is the best/correct way to support client-side certificate authentication using Flurl?


Solution

  • You're close - a custom factory is definitely the way to go. But you want to override CreateMessageHandler rather than CreateClient:

    public class X509HttpFactory : DefaultHttpClientFactory
    {
        private readonly X509Certificate2 _cert;
    
        public X509HttpFactory(X509Certificate2 cert) {
            _cert = cert;
        }
    
        public override HttpMessageHandler CreateMessageHandler() {
            var handler = base.CreateMessageHandler();
            handler.ClientCertificates.Add(_cert);
            return handler;
        }
    }
    

    Then you can either register it globally (on app startup):

    FlurlHttp.Configure(settings => {
        settings.HttpClientFactory = new X509HttpFactory(myCert);
    });
    

    Or for all calls to a particular host:

    FlurlHttp.ConfigureClient(ROOT_URL, cli => {
        cli.Settings.HttpClientFactory = new X509HttpFactory(myCert);
    });
    

    Or for a new FlurlClient:

    var cli = new FlurlClient(url)
        .Configure(settings => settings.HttpClientFactory = new X509HttpFactory(myCert));
    

    Or an existing one:

    cli.Settings.HttpClientFactory = new X509HttpFactory(myCert);