.NET

Secure Self-Hosted WCF REST Services with a Custom UserNamePasswordValidator

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.

Download the code for this blog post here.

Reference: Secure Self-Hosted WCF REST Services with a Custom UserNamePasswordValidator from our NCG partner Tony Sneed at the Tony Sneed’s Blog blog.

Related Articles

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button