WWSAPI to WCF Interop 7: HTTP header authentication (part 3) - used in BasicHttpBinding with transport credential only

In my previous post I explained how to do HTTP header authentication protected by SSL in WWSAPI. In this post, I’ll show how to do header authentication without SSL. In WCF, this mode is called TransportCredentialOnly and is only available in BasicHttpBinding. (Note that this mode is not secure as the SOAP envelope is sent in clear text inside the HTTP entity body. Also, the header authentication mechanisms themselves are not secure even with SSL (subject to forwarding attack), let alone without it. So this mode can only be used in an environment where the network security is provided by means other than WWSAPI)

The following code snippet shows how to set up the security description to do HTTP header authentication without SSL. I use Basic authentication here to show how to use non-default Windows credential. Because Basic authentication requires the username/password be sent in clear text (Base64 encoded) in the HTTP request header, default credential wouldn’t work (Explicit credential is also a requirement for WWSAPI Digest authentication for legacy reason).

      // declare and initialize an explicit Windows credential

      WS_STRING_WINDOWS_INTEGRATED_AUTH_CREDENTIAL explicitCred = {};

      explicitCred.credential.credentialType = WS_STRING_WINDOWS_INTEGRATED_AUTH_CREDENTIAL_TYPE;

      explicitCred.username.chars = L"FooUser";

      explicitCred.username.length = (ULONG)wcslen(explicitCred.username.chars);

      explicitCred.password.chars = L"F00Pwd";

      explicitCred.password.length = (ULONG)wcslen(explicitCred.password.chars);

      // use Basic scheme

      ULONG scheme = WS_HTTP_HEADER_AUTH_SCHEME_BASIC;

      WS_SECURITY_BINDING_PROPERTY bindingProperties[1] = {

            {WS_SECURITY_BINDING_PROPERTY_HTTP_HEADER_AUTH_SCHEME, &scheme, sizeof(scheme)}

      };

      // declare and initialize header authentication binding

      WS_HTTP_HEADER_AUTH_SECURITY_BINDING headerAuthBinding = {};

      headerAuthBinding.binding.bindingType = WS_HTTP_HEADER_AUTH_SECURITY_BINDING_TYPE;

      headerAuthBinding.binding.properties = bindingProperties;

      headerAuthBinding.binding.propertyCount = WsCountOf(bindingProperties);

      headerAuthBinding.clientCredential = &explicitCred.credential;

      // set the protection level to none

      WS_PROTECTION_LEVEL protectionLevel = WS_PROTECTION_LEVEL_NONE;

      WS_SECURITY_PROPERTY securityProperties[1] = {

            {WS_SECURITY_PROPERTY_TRANSPORT_PROTECTION_LEVEL, &protectionLevel, sizeof(protectionLevel)}

      };

      WS_SECURITY_BINDING* securityBindings[1] = { &headerAuthBinding.binding };

      // declare and initialize the security description with the property and binding

      WS_SECURITY_DESCRIPTION securityDescription = {};

      securityDescription.properties = securityProperties;

      securityDescription.propertyCount = WsCountOf(securityProperties);

      securityDescription.securityBindings = &securityBindings[0];

      securityDescription.securityBindingCount = WsCountOf(securityBindings);

One thing to note in the code above is the use of WS_SECURITY_PROPERTY. Following the secure-by-default principle, WWSAPI defaults the transport protection level to WS_PROTECTION_LEVEL_SIGN_AND_ENCRYPT. When doing header authentication without SSL, you have to change the protection level to WS_PROTECTION_LEVEL_NONE; otherwise you will get E_INVALIDARG error due to setting conflict.

Corresponding WCF binding configuration in code is:

    BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);

binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

In config file, it’s the following element:

      <basicHttpBinding>

        <binding name="headerAuth">

          <security mode="TransportCredentialOnly">

            <transport clientCredentialType="Basic"/>

   </security>

        </binding>

      </basicHttpBinding>