Zulfiqar's weblog

Architecture, security & random .Net

Per Endpoint UserName Authentication

Posted by zamd on February 15, 2008


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

Advertisements

2 Responses to “Per Endpoint UserName Authentication”

  1. Michael De Marco said

    having a problem with my custom validator being called. Its not called at all. Can youhelp

  2. […] http://zamd.net/2008/02/15/per-endpoint-username-authentication/ […]

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

 
%d bloggers like this: