Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Client Certificate Mapping Authentication in self-hosted Owin endpoint

Is it possible (and if so, how?) to configure a self hosted owin endpoint to use client certificate mapping authentication with A/D? IIS has this feature link, but so far I have not found an equivalent for self-hosted endpoints.

The way in which I have gotten this to work though (bearing in mind this approach is probably not 100% foolproof), is a 2-step process by using a combination of authenticationSchemeSelectorDelegate and OWIN.

This will choose the appropriate AuthenticationScheme (allowing requests containing a cert through, otherwise deferring to NTLM authentication)

public void Configuration(IAppBuilder appBuilder)
{
    var listener = (HttpListener)appBuilder.Properties[typeof(HttpListener).FullName];
    listener.AuthenticationSchemeSelectorDelegate += AuthenticationSchemeSelectorDelegate;
}

private AuthenticationSchemes AuthenticationSchemeSelectorDelegate(HttpListenerRequest httpRequest)
{
    if (!httpRequest.IsSecureConnection) return AuthenticationSchemes.Ntlm;
    var clientCert = httpRequest.GetClientCertificate();
    if (clientCert == null) return AuthenticationSchemes.Ntlm;
    else return AuthenticationSchemes.Anonymous;
}

This will read the contents of the cert and populate the "server.User" environment variable accordingly

public class CertificateAuthenticator
{
    readonly Func<IDictionary<string, object>, Task> _appFunc;

    public CertificateAuthenticator(Func<IDictionary<string, object>, Task> appFunc)
    {
        _appFunc = appFunc;
    }

    public Task Invoke(IDictionary<string, object> environment)
    {
        // Are we authenticated already (NTLM)
        var user = environment["server.User"] as IPrincipal;
        if (user != null && user.Identity.IsAuthenticated) return _appFunc.Invoke(environment);

        var context = environment["System.Net.HttpListenerContext"] as HttpListenerContext;
        if (context == null) return _appFunc.Invoke(environment);

        var clientCertificate = context.Request.GetClientCertificate();

        // Parse out username from certificate

        var identity = new GenericPrincipal
        (
            new GenericIdentity(username), new string[0]
        );

        environment["server.User"] = identity;
    }
}

Is there not a better/standardized way?

like image 518
FXbeckers Avatar asked Nov 07 '13 09:11

FXbeckers


People also ask

What is client certificate mapping authentication?

Client Certificate Mapping authentication using Active Directory - this method of authentication requires that the IIS 7 server and the client computer are members of an Active Directory domain, and user accounts are stored in Active Directory.

How do I add client authentication to my certificate?

On the taskbar, click Start, and then click Control Panel. In Control Panel, click Programs and Features, and then click Turn Windows Features on or off. Expand Internet Information Services, then select Client Certificate Mapping Authentication, and then click OK.


1 Answers

I have not seen any standard components built for this yet. That said, it should be possible to clean up your code a little:

  • You don't need to downcast to HttpListenerContext to get the client cert. The client cert should already be available in the OWIN environment under "ssl.ClientCertificate". See https://katanaproject.codeplex.com/wikipage?title=OWIN%20Keys. You'll also want to check ssl.ClientCertificateErrors because the cert may not have passed all validation checks.
  • You don't need the AuthenticationSchemeSelectorDelegate code. You could just set the listner.AuthenticationSchemes = NTLM | Anonymous. Then you add a middleware after your cert middleware that returns a 401 if server.User is not valid.
like image 199
Tratcher Avatar answered Oct 08 '22 04:10

Tratcher