Skip to main content

Exception when using WCF Client to Web Service Reference behind a proxy

Are you getting the following exception when calling a simple ASMX service via a WCF service reference from behind a firewall (in this case ISA Server): 

"Server encountered an internal error. For more information, turn off customErrors in the server's .config file."

You'd expect there to be an inner exception, but there isn't.  You can get to the actual exception by enabling all exceptions to cause a breakpoint in VS2005/2008 (Menu: Debug->Exceptions and then click the checkbox at the root of all .Net exceptions - be careful doing this in a web site, though, because a tonne of exceptions get thrown and swallowed by the Asp.Net engine!).

The actual error that is occuring is a WebException: [407] Proxy Authentication Required.

In my case, I've also been using Fiddler to debug the SOAP that's going to and from (if you haven't got it, then get it now!) my machine, and had also noticed that when it was running, the exception would go away.  This is probably because of some behaviour on ISA Server where it will reuse existing credentials for a connection to a particular outgoing domain until a certain expiry time is reached - did throw me a curve however, until I figured that out!

Quick note - if you're looking at writing stuff to leverage TFS via it's web services and want to know how the Team Explorer and the Source Control Explorer do the things they do - it's a good idea to use Fiddler to examine the SOAP.

The problem here is an age old one where you have a proxy (in this case ISA Server) through which all your outgoing network traffic is performed.  In my environment, the ISA Server requires credentials for a user on the domain that is allowed through the proxy.  Without it, the connection will be refused, causing a RemotingException.

Now, in the past this could be solved using the System.Net.CredentialCache.DefaultNetworkCredentials property, and assigning it to a property of the SOAP client proxy that is generated - done.  At first glance, there appears to be nothing as simple as that on the ClientBase<T>-deriving type that the WCF service reference generator in VS creates for you.

Looking closer, however, we see the member ClientBase<T>.ClientCredentials, which has a 'Windows' property, which in turn has a 'ClientCredential' property of type NetworkCredentials.  Thinking that this would solve my problem, I wrote the following code:

  1. MyWebServiceClient client = new MyWebServiceClient();
  2. client.ClientCredentials.Windows.ClientCredential =
  3.     System.Net.CredentialCache.DefaultNetworkCredentials;

I should mention at this point, that the project I'm working on doesn't bind it's web service references using the WCF config files - because I have to bind to a dynamic endpoint.  Anybody working with the configs, however, should still be able to use this post to correct this problem when using the config files. 

Anyway, the exception still gets raised. 

I then tried manually filling out the user name, password and domain, but that didn't work either [bangs head on wall].

Found this MSDN forum where the guy solved his problem using config.  not a great use for me, given that I'm configuring declaratively but there is one nugget in there - where the guy sets the HttpTransportBindingElement.UseDefaultWebProxy property to false, and then specifies the address of the proxy in the HttpTransportBindingElement.ProxyAddress property.

This involved also using the CustomBinding class - which I don't want to use.  I simply want to create a BasicHttpBinding object, set some properties, and have done with it.

So I tried switching off the default proxy behaviour, and then give WCF the address of the default proxy(!) - read it again if you can't see why I think this is ludicrous.

Here's my eventual code:

  1. [TestMethod]
  2. public void TestMethod1()
  3. {
  4.     //create a binding
  5.     BasicHttpBinding binding = new BasicHttpBinding
  6.         (BasicHttpSecurityMode.TransportCredentialOnly);
  7.     binding.Security.Transport.ClientCredentialType =
  8.         HttpClientCredentialType.Ntlm;
  9.     binding.Security.Transport.ProxyCredentialType =
  10.         HttpProxyCredentialType.Ntlm;
  11.     //don't read the system proxy
  12.     binding.UseDefaultWebProxy = false;
  13.     //in my case - the URL I placed here was the same
  14.     //as the one assigned by group policy! (WTF?)
  15.     binding.ProxyAddress =
  16.         new Uri("http://proxy:portnum");
  17.  
  18.     //configure the endpoint address
  19.     EndPointAddress addr =
  20.         new EndPointAddress("http://domain/service.asmx");
  21.  
  22.     //create an instance of the client type generated by the add
  23.     //service reference tool using the binding and the endpoint
  24.     //address that we've configured manually
  25.     MyWebServiceClient c = new MyWebServiceClient(binding, addr);
  26.  
  27.     //make sure that the network credentials are set on the client.
  28.     ClientCredentials creds =
  29.         c.ClientCredentials;
  30.     creds.Windows.ClientCredential =
  31.         CredentialCache.DefaultNetworkCredentials;
  32.     //should bee true by default - but be safe.
  33.     creds.Windows.AllowNtlm = true;
  34.     //if the web service at the other end needs to use these credentials
  35.     //for impersonation or whatever, then this level should be set
  36.     //accordingly.
  37.     creds.Windows.AllowedImpersonationLevel =
  38.         System.Security.Principal.TokenImpersonationLevel.Identification;
  39.  
  40.     //now call the method
  41.     MyWebServiceClient.Foo();
  42. }

And it works fine.

A quick note on this.  I am a great fan of MSDN - typically my first port of call is the F1 help, go to the index, type the class into that, and then browse through.  Typically you get some choice examples, with some extended documentation on the impact of different values of various settings etc.  In some parts of the framework, you even find links out to high-level conceptual topics to see 'where you are' in the overall process of whatever that framework is trying to achieve. 

Not with WCF - the F1 help for all the WCF classes is shoddy.  It has to be the least well-documented significant framework since SMO (don't even get me started on that) - not joined up in any way.

Anyway, I hope this post helps somebody out!

Comments

Popular posts from this blog

Asp.Net 2 and 4 default application pool generates CS0016 IIS7.5

Before I start – if you’ve found a bunch of other articles about this around the net, tried the fixes that are mentioned and still not getting any joy – then read on – you might find this solves your problem. Earlier today I discovered that when I run any ASP.Net 2 or 4 application through IIS7.5 using the default application pools (which use ApplicationPoolIdentity) on Windows 2008 R2 x64 I get an error message similar to this: Server Error in '/MvcApplication31' Application. Compilation ErrorDescription: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS0016: Could not write to output file 'c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\mvcapplication31\222b4fe6\4e80a86\App_global.asax.clb4bsnc.dll' -- 'The directory name is invalid. '
Source Error:[No relevant source lines]
Sou…

Serializing to attributes in WCF with DataContractSerializer

It’s a common problem – you want to return an object from a WCF service as XML, but you either want, or need, to deliver some or all of the property values as XML Attributes instead of XML Elements; but you can’t because the DataContractSerializer doesn’t support attributes (you’re most likely to have seen this StackOverflow QA if you’ve done a web search).  Most likely you’ve then migrated all your WCF service code to using the XmlSerializer (with all the XmlElement/XmlAttribute/XmlType attributes et al) – and you’ve cursed loudly.Well, I’m here to rescue you, because it is possible – and the answer to the problem is actually inferred from the MSDN article entitled ‘Types supported by the Data Contract Serializer’.The example I’m going to give is purely for illustration purposes only.  I don’t have a lot of time, so work with me!Create a new Asp.Net WCF service application, you can use Cassini as your web server (probably easier – otherwise you might have to enable Asp.Net compatibil…

Adding ‘Deny’ functionality to AuthorizeAttribute in Asp.Net Web API

For the web service project I’m working on at the moment I need to be able to treat authorization differently based on the hostname of the URL that requests are made through.To state more clearly – these web services will have a ‘sandbox’ mode in addition to the real mode, and the mode a request will operate under is determined as part of the controller-selection phase early in the Web API request lifecycle.  So, say that my web services will be hosted on services.acme.com; the sandbox will simply be sandbox.services.acme.com.Please note – a discussion of how this is implemented is entirely outside the scope of this article; but I’ll just say that I’ve developed an in-house multi-tenancy layer for both MVC 4 and Web API that allows us to define ‘brands’ and, under those, you can then redefine content, controllers, and even the DI container that is used.These services are going to require caller-level authentication for most operations via SCRAM Authentication (RFC 5802), and as such m…