8
votes

I am accessing a web service using WCF. Using WSHttpBinding, Security mode is set Transport (https) and client credential type is Basic. When I try to access the service using the proxy, getting an 401 unauthorized exception.

Here is the Binding

var binding = new WSHttpBinding()
        {
            UseDefaultWebProxy = true,
            Security =
            {
                Mode = SecurityMode.Transport,
                Transport =
                {
                    ClientCredentialType = HttpClientCredentialType.Basic,
                },
            }
        };

Here is the service call

var client = new InternetClient(binding, new EndpointAddress("httpsurl"));

        client.ClientCredentials.UserName.UserName = "username";
        client.ClientCredentials.UserName.Password = "password";
        client.ProcessMessage("somevalue");

When looked into Http headers using Http Analyzer CONNECT HEADER

(Request-Line):CONNECT somehost.com:443 HTTP/1.1
Host:somehost.com
Proxy-Connection:Keep-Alive

POST HEADER

(Request-Line):POST /Company/1.0 HTTP/1.1
Content-Type:application/soap+xml; charset=utf-8
VsDebuggerCausalityData:uIDPo+voStemjalOv5LtRotFQ7UAAAAAUKLJpa755k6oRwto14BnuE2PDtYKxr9LhfqXFSOo8pEACQAA
Host:somehost.com
Content-Length:898
Expect:100-continue
Connection:Keep-Alive

If you see the header Authorization header is missing

Now my question is why WCF call missing the Authorization header? Am I missing something? . Please ask if you need more information

4

4 Answers

14
votes

This is a common problem, but the situation is different from what you think.

It turns out that initially for the 1st request a WCF client that is configured to use HTTP basic authentication will nevertheless send the request without the necessary Authorization header to the server. This is the default behavior of the HttpWebRequest class used by the WCF client.

Normally, the web service server will then return a HTTP 401 Unauthorized response to the WCF client, upon which the latter will resend the message with the Authorization header. This means under normal conditions for HTTP Basic Authentication there will be a a rather useless round trip to the server.

This also explains why the header was missing in your sniffed message. Some Http sniffs possibly don't pass on the 401 response, so the whole exchange gets messed up.

The server round-trip and dependence on the 401 response can be avoided by manually injecting the required Authorization header into every request. See e.g. how to manually inject Authorization header into WCF request

11
votes

As a slight modification from a previous answer, to support async / await calls, you can actually create a new OperationContext and pass it around on whatever thread you like (as long as it is not shared across concurrent threads as it isn't a thread-safe object)

var client = new MyClient();
client.ClientCredentials.UserName.UserName = "username"; 
client.ClientCredentials.UserName.Password = "password";
var httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(client.ClientCredentials.UserName.UserName + ":" + client.ClientCredentials.UserName.Password));

var context = new OperationContext(ormClient.InnerChannel);
using (new OperationContextScope(context))
{
    context.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
    return await client.SomeMethod();
}
1
votes

Notice the Expect:100-continue in the header. That's the reason for the round trip.

Put this in your web.config and try again:

<system.net>
    <settings>
      <servicePointManager expect100Continue="false"/>
    </settings>
</system.net>
0
votes

Actually, I was wrong about this question. I did see different behaviour when running HTTP analyzer. While Http anaylzer running, my application crashed after receiving 401 response. When Http analyzer application closed, the above code worked as expected.