WCF Authentication: Custom Username and Password Validator

There are a number of authentication techniques supported by WCF. For instance Windows Authentication, X509 Certificates, Issued Tokens, and Username and Password are all mechanisms that can be used for authentication.

These client credential types are configured as part of the binding configuration for an endpoint. For example the config section below customises the netTcpBinding to use an X509 certificate:

<netTcpBinding>

  <binding name="tcpWithMessageSecurity">

    <security mode="Message" >

      <message clientCredentialType="Certificate"/>

    </security>

  </binding>

</netTcpBinding>

If you change the Client Credential Type to “UserName”, you can then pass username and passwords to the service and authenticate the client based on these credentials.

Sending unencrypted username and password over any communication framework is usually not advisable. Therefore, when you opt in for the UserName client credential type, WCF insists that your service must also reference a service certificate that contains a private key. The public key in this certificate is used to protect the confidentiality of the username and password sent to the service. The private key is then used by the service to obtain those encrypted credentials.

If a server certificate is not specified, at service start-up time, you will receive the following exception: “The service certificate is not provided. Specify a service certificate in ServiceCredentials.”

By default, when a user name and password is used for authentication, WCF uses Windows to validate the username and password. Having said that, WCF is one of the world’s most extensible communication frameworks and it should not surprise you if I say that you can take control of the username and password validation:

This first step is to create a “validator” by inheriting from the UserNamePasswordValidator class and override the Validate method (Add a reference to System.IdentityModel.dll first):

public class CustomUserNameValidator : UserNamePasswordValidator

{

 public override void

  Validate(string userName, string password)

 {

  // perform the validation logic

 }

The next step is to configure the service to use this custom validator. As this is the local behavior of the service, you will need to specify the custom validator as part of your service behavior configuration:

<serviceBehaviors>

  <behavior name="CustomValidator">

    <serviceCredentials>

      <userNameAuthentication

        userNamePasswordValidationMode="Custom"

     customUserNamePasswordValidatorType=

           "MyAssembly.CustomUserNameValidator, MyAssembly"/>

      <serviceCertificate

        findValue="localhost"

        x509FindType="FindBySubjectName"

        storeLocation="CurrentUser"

        storeName="My" />

    </serviceCredentials>

  </behavior>

As you can see, there are two elements defined as part of the serviceCredentials section. The userNameAuthentication element specifies that a custom validator is used and the serviceCertificate element provides a reference to the X509 certificate used by the service to ensure the integrity of the client credentials. See here for information on how to create a temporary certificate for use during development.

Do not forget to set service’s behaviourConfiguration property to the name of the service behavior defined above:

<service name="..." behaviorConfiguration="CustomValidator">

Both client and service endpoints need to enable Username and Password authentication mode on their bindings:

<netTcpBinding>

  <binding name="tcpWithMessageSecurity">

    <security mode="Message" >

      <message clientCredentialType = "UserName" />

    </security>

  </binding>

</netTcpBinding>

Our service is now fully configured. The final step is to provide the username and password to the client proxy which can then be encrypted and sent to the service. If you are using the ChannelFactory directly to create a proxy then use the code below to set the username and password programmatically:

ChannelFactory<ISimpleService> factory =

 new ChannelFactory<ISimpleService>(endpointConfigurationName);

factory.Credentials.UserName.UserName = "Pedram";

factory.Credentials.UserName.Password = "Password01";

However, if SvcUtil or Visual Studio has generated a proxy for you, use the code below to set the username and password:

proxy.ClientCredentials.UserName.UserName = "Pedram";

proxy.ClientCredentials.UserName.Password = "Password01";

 

Please note that there is no way to set the username and password in config.

Depending on the type of service certificate, you may need to specify a DNS identity for your client endpoints. This is used when the client authenticates the service to ensure that it is talking to the expected service and not any other hoax service intercepting your calls to the genuine service. The exception below is an indication of this:

“Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was 'X' but the remote endpoint provided DNS claim 'Y'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity 'Y' as the Identity property of EndpointAddress when creating channel proxy.”

You can modify the client endpoint to include an identity element such as the one below (see here for more information on service identity):

<identity>

  <dns value="pedramr"/>

</identity>

You are now ready to test the service.

You can also download a sample application which tries this technique over netTcp and wsHttp bindings from here (built using Visual Sudio 2008 beta 2 and is compatible with .NET Framework 3.0). Follow the steps below before running the sample application:

- Create a certificate

- Modify the serviveCertificate elements defined in both client and service App.config files to reference your new certificate (you may decide to change the FindType as well)

- Specify a correct DNS identity value for the client endpoints

Let me know what you thought of this article...

References:

- Selecting a WCF Credential type

- Bypassing certificate validation

- Common WCF Security Scenarios

- Transport Security with Basic Authentication

- Service Identity and Authentication

- How To Use the ASP.NET Membership Provider

WCFSecurity.zip