Pedro Félix's shared memory

WCF Web API – Self-hosting, HTTPS and HTTP Basic Authentication

(Edited on 02/26/2012: WCF Web API is now ASP.NET Web API, which introduced some breaking changes. However, I’ve a new post describing how to enable HTTPS with self-hosting, on the ASP.NET Web API Beta)

(Edited on 09/24/2011: There is a new version of the code below, for the Preview 5 release. The major changes are on the configuration model.)

This is the third post on a series about the new WCF Web API – Preview 4.

In the last post, I described how to create self hosted services. In this post, I show how to use HTTPS and Basic Authentication for these services.

Self-hosting and HTTPS

Both self-hosting and IIS-hosting use the HTTP.sys windows component for HTTP request listening. This HTTP.sys component handles the transport level issues, such as the SSL protocol.

One of the configurations that must be done at the HTTP.sys level is the definition of the server certificate and private key, since the SSL protocol handshake is performed by this component.

When using IIS, the HTTP.sys configuration is done via the IIS manager. However, when self-hosting, the netsh command line utility must be used.

So, the first step is to register the server side certificate using the following command

netsh http add sslcert ipport=0.0.0.0:port certhash=thumbprint appid={app-guid}

where

This command allows for other optional parameters, such as clientcertnegotiation, that define complementary SSL aspects.

For more information about this subject, I recommend:

Endpoints and HTTPS

In order for a WCF endpoint to use HTTPS, instead of plain HTTP, the endpoint’s binding must be properly defined. The new HttpBinding has a constructor overload that receives an HttpBindingSecurityMode. The options are:

Endpoints and HTTP Basic Authentication

The authentication mechanism (e.g. HTTP Basic Authentication or client certificates) is also defined at the binding, via the Security.Transport.ClientCredentialType property.

The credential validation policy, username and password in the case of HTTP Basic Authentication, is defined at the service host, using the Credentials.UserNameAuthentication property.

The following code excerpt shows both the binding and host configuration.


using (var host = new HttpServiceHost(typeof(TheResourceClass), new string[0]))
{
    var binding = new HttpBinding(HttpBindingSecurityMode.Transport);
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
    var ep = host.AddServiceEndpoint(typeof(TheResourceClass),
                                                             binding,
                                                             "https://localhost:8435/greet");
    ep.Behaviors.Add(new HttpBehavior()
        {
            OperationHandlerFactory = new MyOperationHandlerFactory()
        });
    host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode =
    UserNamePasswordValidationMode.Custom;
    host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new MyCustomValidator();
    host.Open();
    Console.WriteLine("Service is opened, press any key to continue");
    Console.ReadKey();
}

The custom validator derives from UserNamePasswordValidator


class MyCustomValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        if(!credentials are valid)
            throw new FaultException("Unknown Username or Incorrect Password");
    }
}

Accessing the user’s identity

The final issue regards the access to the user’s identity, obtained from the authentication process.  It would be nice if the request handling operation could receive this information as a parameter.


[ServiceContract]
class TheResourceClass
{
    [WebGet(UriTemplate = "")]
    HttpResponseMessage GetGreetings(IPrincipal principal)
    {
        return new HttpResponseMessage()
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent("hello "+principal.Identity.Name)
        };
    }
}

In above example, the GetGreetings operation receives an IPrincipal with the user’s identity. This parameter is injected into the operation by an operation handler, which is a new WCF Web API concept.  This new concept alone deserves one or more future posts, so here I will only present the required code.


class PrincipalFromBasicAuthenticationOperationHandler : HttpOperationHandler<HttpRequestMessage,IPrincipal>
{
    public PrincipalFromBasicAuthenticationOperationHandler() : base("Principal") { }

    public override IPrincipal OnHandle(HttpRequestMessage input)
    {
        if (input.Headers.Authorization == null || input.Headers.Authorization.Scheme != "Basic")
        {
            // If properly configured, this should never happen:
            // this OperationHandler should only be used when
            // Basic authorization is required
            throw new HttpResponseException(HttpStatusCode.InternalServerError);
        }
        var encoded = input.Headers.Authorization.Parameter;
        var encoding = Encoding.GetEncoding("iso-8859-1");
        var userPass = encoding.GetString(Convert.FromBase64String(encoded));
        int sep = userPass.IndexOf(':');
        var username = userPass.Substring(0, sep);
        var identity = new GenericIdentity(username, "Basic");
        return new GenericPrincipal(identity, new string[] { });
    }
}

The above operation handler extracts the user name from the “Authorization” header, whose credentials were previously validated via the custom validator, and creates a GenericPrincipal with that name.

Next, an operation handler factory is required to register this new operation handler.


class MyOperationHandlerFactory : HttpOperationHandlerFactory
{
    protected override Collection<HttpOperationHandler> OnCreateRequestHandlers(ServiceEndpoint endpoint, HttpOperationDescription operation)
    {
        var coll = base.OnCreateRequestHandlers(endpoint, operation);
        if (operation.InputParameters.Any(p => p.Type.Equals(typeof(IPrincipal))))
        {
            var binding = endpoint.Binding as HttpBinding;
            if (binding != null && binding.Security.Transport.ClientCredentialType == HttpClientCredentialType.Basic)
            {
                coll.Add(new PrincipalFromBasicAuthenticationOperationHandler());
            }
        }
        return coll;
    }
}

Notice that the PrincipalFromBasicAuthenticationOperationHandler is only inserted if the operation requires an IPrincipal and the endpoint is using HTTP Basic Authentication.

Finally, the factory is configured into the HttpBehavior used when defining the endpoint.


var ep = host.AddServiceEndpoint(typeof(TheResourceClass), binding, "https://localhost:8435/greet");
ep.Behaviors.Add(new HttpBehavior()
    {
        OperationHandlerFactory = new MyOperationHandlerFactory()
    });

Concluding remarks