Secure Self-Hosted WCF REST Services with a Custom UserNamePasswordValidator

Download the code for this blog post here.

When securing WCF services you’re faced with a choice: Message versus Transport security. Unless you need to conceal messages from an intermediary, your best bet is to stick with transport security and use SSL to secure messages traveling over HTTP.  Using SSL is generally the best choice for ensuring point-to-point privacy and integrity, which lets you pass user credentials over the wire when directly invoking service operations.  This means you can eschew the complexity and overhead of message-based security in favor of the simpler and leaner model of transport-based security.

Once you’ve settled on the option of transport security, there’s the issue of which authentication mode to use.  In most B2B scenarios, it makes sense to go with X509 certificates for client authentication, but that also places demands on clients to sign messages using the certificate.  Another possibility is plain old shared-secret authentication where you might look up usernames and passwords in a database in order to authenticate requests.  WCF has terrific support for this scenario and allows you to supply a custom UserNamePasswordValidator, which you can use to validate client credentials.  Here is an example of a class that extends UserNamePasswordValidator by validating passwords against a hard-coded value.  (Of course, you’ll want to employ a more sophisticated technique, such as hashing the password to compare against entries in a database table.)

public class PasswordValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        if (string.Equals(userName, "Alice", StringComparison.OrdinalIgnoreCase)
            && password == "Password123!@#") return;
        throw new SecurityTokenValidationException();
    }
}

You’ll need to set the security mode of the basic HTTP binding to “TransportWithMessageCredential.” This will cause the service to look in a soap header for client credentials. Then you’ll want to add a serviceCredentials behavior that sets the validation mode to “Custom” and specifies PasswordValidator as the validator type.

<serviceBehaviors>
  <behavior>
    <serviceCredentials>
      <userNameAuthentication userNamePasswordValidationMode="Custom"
          customUserNamePasswordValidatorType="Security.PasswordValidator, Security"/>
    </serviceCredentials>
  </behavior>
</serviceBehaviors>

This is all fine and dandy, but it assumes that clients will only be talking Soap – what about REST-ful clients who don’t know a thing about Soap? It turns out the serviceCredentials behavior doesn’t really have much to do with whether it’s a Soap or Rest-based service.  To authenticate REST clients, you can set the security mode of the web http binding to “Transport” and specify a client credential type of “Basic.”

<webHttpBinding>
  <binding>
    <security mode="Transport">
      <transport clientCredentialType="Basic"/>
    </security>
  </binding>
</webHttpBinding>

Note that this technique will only work when your service is self-hosted (for example, using a Windows Service).  In this case, WCF will hand off authentication to your custom UserNamePasswordValidator type.  However, when hosting in IIS, WCF will allow IIS to handle basic authentication using Windows accounts, and you’ll need a different approach, such as setting the client credential type to None and handling the authentication yourself.

Once you’ve enabled Basic authentication in your self-hosted WCF service, it’s up to the client to set the Authorization header to “Basic” with a username:password string that is base64 encoded.

private static string GetAuthHeader(string userName, string password)
{
    string userNamePassword = Convert.ToBase64String
        (new UTF8Encoding().GetBytes(string.Format("{0}:{1}", userName, password)));
    return string.Format("Basic {0}", userNamePassword);
}

To enable SSL for self-hosted services you’ll need to perform a few extra steps.  First, open an admin command prompt and bind the certificate to the port you’ll be using.  You’ll need to inspect the certificate details (use the Certificates MMC snap-in) to copy the thumbprint, remove the spaces and set the certHash parameter to it. Any arbitrary guid will work for the appId parameter.

netsh http add sslcert ipport=0.0.0.0:2345 certhash=a66c5ed2b8ab91de21d637c6f9a13fd45a8ba92a appid={646937c0-1042-4e81-a3b6-47d678d68ba9}

You may also need to grant permission for the process hosting your service to register a url with Http.Sys.

netsh http add urlacl url=https://+:2345/ user=NetworkService

This assumes you’re running Windows Vista or later.  For earlier versions of Windows you will need to use httpcfg – see here for more info.

The nice thing about WCF is its unified programming model, which allows you to use the same username / password validator for both Soap and Rest clients.  Download and go through the code for this blog post to see it all in action.  Enjoy.

About Tony Sneed

Married with three children.
This entry was posted in Technical and tagged , , . Bookmark the permalink.

8 Responses to Secure Self-Hosted WCF REST Services with a Custom UserNamePasswordValidator

  1. THis does not work in IIS. Basic in WCF means also Basic in IIS. And then IIS will authenticate against Windows accounts only. The validator won’t even get called.

  2. Ah – and the encoding for the header is also not correct. Need to look at my code, but it’s not ASCII…

  3. Ian Hannah says:

    An excellent article but I have one question. I want to play with basic authentication without using a certificate i.e. over HTTP and not HTTPs. However, in order to use the user name and password validation I must have the security mode set to transport otherwise it will not get called and so this means that I must use HTTPs! Is there a way to use the validation without the need for a certificate?

    Thanks
    Ian

    • Tony Sneed says:

      This should be covered in the blog article, but it only works for self-hosted WCF services, for ex, a Windows Service. If hosted in IIS you’re out of luck – your validator will not be called.

      • Ian Hannah says:

        Hi Tony,
        I have it all working now. What I now want to do is use the validate method to call a stored procedure in our database which returns a user id. I need to user the user id in all my web service methods.

        What is the best way to “store” the user id in the validate method and use it in my methods?

        Thanks
        Ian

  4. Andre says:

    Hi Tony,
    This is a great example and its really helped me out. I do have one problem. After I put the certs in the store(s) Local Computer in Trusted Root and personal. When I reboot i have to rebind it with netsh http add sslcert . Have you found a workaround?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s