Authenticating a WCF web service call with .NET 4.0

Posted: (EET/GMT+2)

 

Assume the following: you need to call a .NET based (Windows Communication Foundation) web service (.asmx or .svc) from your C# application built with Visual Studio 2010 and .NET 4.0. You create a service reference to the service, but then start scratching your head about the authentication when using the generated client class.

If you are new to WCF, with little browsing you might find a solution like the following:

MyWebServiceClient client = new MyWebServiceClient();
client.ClientCredentials.UserName.UserName = "John Doe";
client.ClientCredentials.UserName.Password = "...";

This seems to be well until you actually call the web service. If the web service is using HTTP basic authentication (not secure as it uses plain-text for credential transfers, but okay with HTTPS), then you will get an error message like this:

System.ServiceModel.Security.MessageSecurityException:
"The HTTP request is unauthorized with client authentication
scheme 'Anonymous'. The authentication header received from the server was 'Basic realm="my-server-name"'."

And, the remote server returns "401 Unauthorized." in the inner exception. What is wrong?

The basic thing to remember with WCF is that it is run-time configurable. Thus, many different things are actually set in app.config/web.config, depending on your application type. Assuming a desktop application, your app.config might contain something to the following effect:

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="MyWebServiceSoap"
            ...
            useDefaultWebProxy="true">
              ...
              <security mode="None">
                <transport clientCredentialType="None"
                proxyCredentialType="None" realm="" />
                <message clientCredentialType="UserName"
                algorithmSuite="Default" />
              </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://my-server/some-service.asmx"
        binding="basicHttpBinding"
        bindingConfiguration="MyWebServiceSoap"
        contract="MyServiceReference.MyWebServiceSoap"
        name="MyWebServiceSoap" />
    </client>
</system.serviceModel>

By default, no particular security settings have been set, and thus the client code fails, even when you have given the username and password in code.

The solution is to properly modify the app.config section. Here is an example using the HTTP basic authentication scheme:

<security mode="TransportCredentialOnly">
  <transport clientCredentialType="Basic"
  proxyCredentialType="Basic" realm="" />
  <message clientCredentialType="UserName"
  algorithmSuite="Default" />
</security>

This Security section replaces the original one. The correct location is inside the configuration/system.serviceModel/bindings/basicHttpBinding/ binding node.

Good luck!