Per Endpoint UserName Authentication
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