Niraj Bhatt – Architect’s Blog

Ruminations on .NET, Architecture & Design

Username Authentication over basicHttpBinding with WCF’s ChannelFactory Interface

HTTP/HTTPS holds good (add no session management) for lot of people today & they prefer using them as their transport protocol for WCF Services. Many a time users connecting with your service are outside your domain so you will like to authenticate them with username / password. This is where WCF makes things difficult.

There is some support shown here for self hosted service (but I have my own reservations over that as IIS is the most preferred hosting mechanism when it comes to HTTP). Let me split this post into 2 parts – assigning credentials to client & authenticating those credentials on server (N.B. my writing is on top of custom binding + channel interface, no Add Service Reference used).

1) Adding credentials on client side via channel factory interface:

ClientCredentials is part of System.ServiceModel.Description.ClientCredentials (System.ServiceModel). Problem is ChannelFactory has a readonly ClientCredentials Property (not when you generate proxy by inheriting from ClientBase this is simplified for you). Luckily there is a workaround. To start with you can instantiate it & assign required values

ClientCredentials loginCredentials = new ClientCredentials();
loginCredentials.UserName.UserName = “Niraj”;
loginCredentials.UserName.Password = “Password123″;

Now ClientCredentials are by default endpoint behaviors, so you have remove the default one & attach the newly created one. Below code shows that

var defaultCredentials = factory.Endpoint.Behaviors.Find<ClientCredentials>();
factory.Endpoint.Behaviors.Remove(defaultCredentials); //remove default ones
factory.Endpoint.Behaviors.Add(loginCredentials); //add required ones

 2) Authenticating Credentials on Server Side: This is where things get tricky and you need to use something probably you have heard of but haven’t tried yet, a WCF security mode called – TransportWithMessageCredential (N.B. It’s not currently possible to handle this scenario using Basic Authentication of IIS (transport clientCredentialType), though you can easily do it for REST).

Step 1 is your customize your binding (this has to be done on both Server & Client configurations)

<bindings>
      <basicHttpBinding>
        <binding name=”usernameHttps”>
          <security mode=”TransportWithMessageCredential”>
            <message clientCredentialType=”UserName”/>
          </security>
        </binding>
      </basicHttpBinding>
</bindings>

Note that the above would force you to use HTTPS but anyways that’s a recommended approach also. Now it’s time to go to your service and specify the service credentials as shown below, here we use custom authenticator but you could as well used provider model of ASP.NET,

<serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode=”Custom”
              customUserNamePasswordValidatorType=”…” />
</serviceCredentials>

public class CustomPass : UserNamePasswordValidator //System.IdentityModel.dll
{
        public override void Validate(string userName, string password)
        {
            //your validation here, throw exception if invalid user
        }
}

That’s it. You are all set. Fire you client & you have username authentication over HTTPS. Hope that helps.

(N.B. You can check my blog entry about how to configure HTTPS for WCF on IIS)

May 22, 2009 Posted by nirajrules | Windows Communciation Foundation | , | 2 Comments

Resolving XmlDictionaryReaderQuotas Error for WCF Compression using GZipEncoder with Custom Binding

To compress the WCF data transfered over the wire, Microsoft Samples contains a GZipEncoder. This encoder wraps TextMessagingEncoder applying GZip Compression on top of it (N.B. you can even wrap a Binary Encoder to enable compression on Images for instance).

But as you try to transfer a large object using GZipEncoder, you will run into the below error asking to increase maximum string length content:

Unhandled Exception: System.ServiceModel.CommunicationException: Error in deserializing body of reply message for operation ‘GetData’. The maximum string content length quota (8192) has been exceeded while reading XML data. This quota may be increased by changing the MaxStringContentLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 192, position 30. — System.Xml.XmlException: The maximum string content length quota (8192) has been exceeded while reading XML data. This quota may be increased by changing the MaxStringContentLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader.

This can look quite confusing if you are not familiar with how WCF Channel pipeline works. This error is encountered by many even if you are not using GZipEncoder outlined above. So let’s look at all solutions while using & not using GzipEncoder (below I set the limit to max allowed, though it’s not recommended) :

1) Standard Binding (No GZipEncoder)
<bindings>
      <basicHttpBinding>
        <binding>
          <readerQuotas maxStringContentLength=”2147483647″/>
        </binding>
      </basicHttpBinding>
</bindings>
//new BasicHttpBinding().ReaderQuotas.MaxStringContentLength = Int32.MaxValue
N.B. Binding is a collection of channels providing an abstract way to add readerQuotas.

2) Custom Binding (No GZipEncoder)
<bindings>
      <customBinding>
        <binding>
          <textMessageEncoding>
            <readerQuotas maxStringContentLength=”2147483647″/>
          </textMessageEncoding>
          <httpTransport />
        </binding>
      </customBinding>
</bindings>

/*
CustomBinding binding = new CustomBinding();
TextMessageEncodingBindingElement element = new TextMessageEncodingBindingElement();
element.ReaderQuotas.MaxStringContentLength = Int32.MaxValue;
binding.Elements.Add(element);

*/
N.B. For CustomBinding you need to select channels manually and for encoding channel you can specify the readerQuotas.

3) Using GZipEncoder – in this case you need to add couple of lines in GZipMessageEncodingBindingElement class (GZipMessageEncodingBindingElement.cs file). The method which you would change is below:

public override IChannelFactory BuildChannelFactory (BindingContext context)
{
if (context == null)
throw new ArgumentNullException(“context”);
context.BindingParameters.Add(this);

var property = GetProperty<XmlDictionaryReaderQuotas>(context);
property.MaxStringContentLength = 2147483647; //Int32.MaxValue
property.MaxArrayLength = 2147483647;
property.MaxBytesPerRead = 2147483647;

return context.BuildInnerChannelFactory();
}

N.B. It’s not possible to alter these parameters through configuration file while using GZipEncoder.

Hope this helps :) .

December 22, 2008 Posted by nirajrules | Windows Communciation Foundation | , , | 2 Comments