When you choose to use Client Certificate as your client credential over SSL, you want to retrieve the x509 Certificate on the server side to do authorization. It is very simple with the latest Web API bits. If you have access to the request message anywhere in the pipeline, such as message handler, action filter or action. you can simply call the extension method shown below. Please be careful that the GetClientCertificate method might return null if the client does not send an valid certificate.

Now getting the client certificate is easy, but how to set it up correctly on self host and web host is not that easy. The following instructions walk you through the process step by step.

Web Host

Step 1: go to the IIS manager, SSL setting, check require client certificate

Step 2: register a custom message handler and verify that the client certificate is something you expect.

Code Snippet
  1. public class CustomCertificateMessageHandler : DelegatingHandler
  2.     {
  3.         protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  4.         {
  5.             X509Certificate cert = request.GetClientCertificate();
  6.             if (cert != null)
  7.             {
  8.                 if (cert.Subject.Contains("Some Name you are expecting"))
  9.                 {
  10.                     Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(cert.Subject), new[] { "Administrators" });
  11.                 }
  12.             }
  13.  
  14.             return base.SendAsync(request, cancellationToken);
  15.         }
  16.     }

Step 3: register the custom message handler.

Code Snippet
  1. // Register a message to turn the client cert to a admin role
  2.             GlobalConfiguration.Configuration.MessageHandlers.Add(new CustomCertificateMessageHandler());

 

Step 4: Write a RequireAdminAttribute

Code Snippet
  1. public class RequireAdminAttribute : AuthorizationFilterAttribute
  2.     {
  3.         public override void OnAuthorization(HttpActionContext context)
  4.         {
  5.             // do authorization based on the principle.
  6.             IPrincipal principal = Thread.CurrentPrincipal;
  7.             if (principal == null || !principal.IsInRole("Administrators"))
  8.             {
  9.                 context.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
  10.             }
  11.         }
  12.     }

Step 5: Add the RequireAdminAttribute to the action.

Code Snippet
  1. public class HomeController : ApiController
  2.     {
  3.         [RequireAdmin]
  4.         public HttpResponseMessage Get()
  5.         {
  6.             return new HttpResponseMessage(HttpStatusCode.OK)
  7.             {
  8.                 Content = new StringContent("Default User")
  9.             };
  10.         }
  11.  
  12.         public HttpResponseMessage Post()
  13.         {
  14.             return new HttpResponseMessage(HttpStatusCode.OK)
  15.             {
  16.                 Content = new StringContent("User Posted")
  17.             };
  18.         }
  19.     }

Now your GET action can only be accessed with the administrators.

Self Host

Step 1: Tell the WCF transport, I want to client to send certificate over SSL.

Code Snippet
  1.             // client cert
  2.             config.ClientCredentialType = HttpClientCredentialType.Certificate;

Step 2: Write some client code to pick a certificate to send

Code Snippet
  1.                 WebRequestHandler handler = new WebRequestHandler();
  2.                 handler.ClientCertificateOptions = ClientCertificateOption.Manual; // this would pick from the Current user store
  3.                 handler.ClientCertificates.Add(GetClientCertificate());
  4.                 handler.UseDefaultCredentials = true;
  5.                 HttpClient client = new HttpClient(handler);

Implement the GetClientCertificate to retrieve a certificate programmatically.

Then repeat step 2-5 from the web host section to complete the experience.

Hope this helps.