In this post I will explain an extensibility mechanism to support multiple userName/password validation modes in the same service. 

When you use UserName authentication in WCF there are couple of different mode for the actual UserName/Password validation. These modes are:

·         Windows: Credentials are verified against a windows account (either local or domain).

·         Custom: Credentials are verified by calling your custom UserNamePasswordValidator component.

·         Membership: Credentials are verified by using SqlMembershipProvider.

You can configure userName authentication and the actual validation mode using the following service behavior:

<serviceCredentials>

  <serviceCertificate findValue="01 0a 27" storeName="My" x509FindType="FindByThumbprint"/>

  <userNameAuthentication userNamePasswordValidationMode="Custom"

                          customUserNamePasswordValidatorType="UserNameValidationSamples"/>

</serviceCredentials>

Using this behavior however will results in the same validation mode (custom in above case) being used for all the endpoints. Sometimes there is a requirement to support different mode of UserName/Password validation for different endpoints belonging to the same service. Out of the box WCF doesn't provide this functionality so I have created a custom endpoint behavior and a behavior extension to achieve this functionality.

The implementation of my custom behavior clones the ServiceCredentials behavior and overrides the value of userNamePasswordValidationMode with the endpoint's mode value.

namespace UserNameValidationSample

{

    //behavior configuration extension element

    public class UserNameValidationBehaviorElement : BehaviorExtensionElement

    {

        public UserNameValidationBehaviorElement()

        {

        }

        public override Type BehaviorType

        {

            get { return typeof(UserNameValidationBehavior); }

        }

        protected override object CreateBehavior()

        {

            return new UserNameValidationBehavior(this.Mode);

        }

        [ConfigurationProperty("mode")]

        public UserNamePasswordValidationMode Mode

        {

            get

            {

                return (UserNamePasswordValidationMode)base["mode"];

            }

            set

            {

                base["mode"] = value;

            }

        }

        ConfigurationPropertyCollection properties = null;

        protected override ConfigurationPropertyCollection Properties

        {

            get

            {

                if (this.properties == null)

                {

                    ConfigurationPropertyCollection propertys = new ConfigurationPropertyCollection();

                    propertys.Add(

                                    new ConfigurationProperty("mode", typeof(UserNamePasswordValidationMode), null,

                                                                              ConfigurationPropertyOptions.IsRequired));

                    this.properties = propertys;

                }

                return this.properties;

            }

        } 

    }

    public class UserNameValidationBehavior : IEndpointBehavior

    {

        UserNamePasswordValidationMode mode;

        public UserNameValidationBehavior(UserNamePasswordValidationMode mode)

        {

            mode = mode;

        }

        #region IEndpointBehavior Members

 

        public void AddBindingParameters(ServiceEndpoint endpoint,

                System.ServiceModel.Channels.BindingParameterCollection bindingParameters)

        {

            // override the validation mode for this endpoint. 

            var sc = bindingParameters.Remove<ServiceCredentials>();

            if (sc != null)

            {

                var scCopy = sc.Clone();

                scCopy.UserNameAuthentication.UserNamePasswordValidationMode = mode; 

                bindingParameters.Add(scCopy);

            }

        } 

        public void ApplyClientBehavior(ServiceEndpoint endpoint,

                               System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)

        {} 

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint,

                 System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)

        {} 

        public void Validate(ServiceEndpoint endpoint)

        {} 

        #endregion

    }

}

This is how you define and use a the behavior from config file:

<system.serviceModel>

  <services>

    <service name="UserNameValidationSamples.CalculatorService" behaviorConfiguration="credBehavior">

      <endpoint address="http://localhost:8080/CalculatorService/Custom"

                binding="wsHttpBinding"

                bindingConfiguration="noSecureConversation"

                contract="UserNameValidationSamples.ICalculatorService"

                behaviorConfiguration="customValidation"

                />

      <endpoint address="http://localhost:8080/CalculatorService/Windows"

                binding="wsHttpBinding"

                bindingConfiguration="noSecureConversation"

                contract="UserNameValidationSamples.ICalculatorService"

                behaviorConfiguration="windowsValidation"

                />

    </service>

  </services> 

  <behaviors>

    <endpointBehaviors>

      <!-- my custom behavior -->

      <behavior name="customValidation">

        <userNameValidation mode="Custom"/>

        <!-- Custom, Windows, Membership-->

      </behavior> 

      <behavior name="windowsValidation">

        <userNameValidation mode="Windows"/>

        <!-- Custom, Windows, Membership-->

      </behavior> 

    </endpointBehaviors>

  </behaviors> 

  <!-- create a userNameValidation extension -->

  <extensions>

    <behaviorExtensions>

      <add name="userNameValidation" type="UserNameValidationSamples.UserNameValidationBehaviorElement"/>

    </behaviorExtensions>

  </extensions>

</system.serviceModel>

Source code of a VS2008 project is attached with this post.

Download: usernamesamples.zip