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

  • port is the listening port (e.g. 443); the special IP address 0.0.0.0 matches any IP address for the local machine;
  • thumbprint is the certificate’s SHA-1 hash, represented in hexadecimal;
  • app-guid is any GUID, used to identity the owning application.

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:

  • None – no security;
  • Transport –both authentication and transport level protection, i. e., HTTPS;
  • TransportCredentialOnly – only authentication.

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

  • Security is one of the WCF Web API where more changes are expected in the future. For example, the current sample uses the built in support for HTTP Basic Authentication provided by the “old” WCF HTTP transport channel. In the future, this functionality may be provided by other means (e. g. via a HttpMessageChannel/HttpMessageHandler).
  • Configuration is another area expected to change in the future. In this sample, I configure the endpoint directly, via the HttpBehavior. In the future, this may not be the preferred or adequate way to do this.
  • Unfortunately, the way Basic Authentication is currently implemented by the transport channel has the following issue: after the first invalid (username, password) pair, the response will be 403 and not 401. This means that the password would not be requested again by a typical user-agent. See  http://blogs.msdn.com/b/drnick/archive/2010/02/02/fix-to-allow-customizing-the-status-code-when-validation-fails.aspx for more details.

10 thoughts on “WCF Web API – Self-hosting, HTTPS and HTTP Basic Authentication

  1. Pingback: HTTP content classes « Pedro Félix’s shared memory

  2. Tony

    Hi Pedro Felix,
    I have been using this code (new version) and I got it working for a “GET” method. But, when I try this…

    [WebInvoke(UriTemplate = “”, Method = “POST”)]
    public HttpResponseMessage SendMessage(IPrincipal principal, Message message)
    {

    I get this error:

    The HttpOperationHandlerFactory is unable to determine the input parameter that should be associated with the request message content for service operation ‘SendMessage’. If the operation does not expect content in the request message use the HTTP GET method with the operation. Otherwise, ensure that one input parameter either has it’s IsContentParameter property set to ‘True’ or is a type that is assignable to one of the following: HttpContent, ObjectContent`1, HttpRequestMessage or HttpRequestMessage`1.

    Any ideas?

    Reply
  3. mj1856

    How about an example of using ssl when using IIS instead of self-hosting? I thought it would be trivial, but I’m having lots of problems. Thanks in advance.

    Reply
  4. Lars Morten Nygaard (@LarsM_Nygaard)

    Excellent article Pedro, thanks a lot.

    There seems to be a couple of updates in the API since you published.

    – OnHandle in HttpOperationHandler is now protected, thus the override must be the same.

    – The Type property of HttpParameter should be ParameterType, giving this lambda in MyOperationsHandler:

    …………. operation.InputParameters.Any(p => p.ParameterType.Equals…………

    mj1856 – I have a working IIS project, what issues are you facing?

    Reply
  5. Pingback: HTTPS and Basic Authentication without Operationshandler | Coders & Admins

  6. Pingback: R&D | Study Notes for 70-487 – Developing Windows Azure and Web Services

Leave a reply to Sanousy Howari Cancel reply