Recently we developed a WCF service, which had to be hosted in IIS. This service must run under the credentials of the user calling the service, so we used Integrated Authentication with Impersonation. Even the service was hosted in IIS, we were not using ASP.Net compatibility mode, so the WCF was responsible to do all the work related to authentication and impersonation.
At the client, we generated the proxy using Add Service Reference (to use WCF at client side), and we started our tests. Here is the configuration used at the client
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IMyService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://myserver/webServices/MyService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyService"
contract="TestConsole.mySvc.IMyService" name="BasicHttpBinding_IMyService" />
</client>
</system.serviceModel>
</configuration>
Our first test passed with success. However, after some resets in IIS we got un exception when calling the service from the client
Could not load file or assembly 'MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fa47a5aeac7aa410' or one of its dependencies. Access is denied.
Without understanding the source and the cause of this random error, we began our research. We tried to change ACLs, we tried to change settings on the Application Pool, we tried everything you can imagine, and after a few days of trial and error, we discovered the pattern of the error. So, we got success on the service call after cleaning all the Temporary ASP.Net File and resetting the IIS. In this condition the service ran ok, until the next compilation, maybe caused by a modification in the web.config file. After the new compilation we got the exception again. I almost quit hosting my service in IIS, and change to a self-hosted service, using a windows service. However, before quit, I decided to ask for help to one of the Portuguese WCF Gurus, António Cruz. A few hours later, António call me, and told me that the problem was in the client side, because we were not specifying the allowed impersonation level, and the default is Identification,which means (pasting from documentation)
The server process can obtain information about the client, such as security identifiers and privileges, but it cannot impersonate the client. This is useful for servers that export their own objects, for example, database products that export tables and views. Using the retrieved client-security information, the server can make access-validation decisions without being able to use other services that are using the client's security context.
António told me to use the level Impersonation instead, which means (pasting again)
The server process can impersonate the client's security context on its local system. The server cannot impersonate the client on remote systems.
To do this we had to specify a behavior. Here is the new configuration file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IMyService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://ag-bfcamara/webServices/MyService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyService"
contract="TestConsole.mySvc.IMyService" name="BasicHttpBinding_IMyService"
behaviorConfiguration="ImpersonationBehavior" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="ImpersonationBehavior">
<clientCredentials>
<windows allowedImpersonationLevel="Impersonation" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
With this configuration we eliminate the strange behaviour we had before and everything is now ok. And this security feature makes sense, since the client has the right to specify what kind of impersonation the service can do. However, when using Identification level (which is the default), why the service runs ok if we delete the temporary ASP.Net files and reset IIS? It seems there is a special condition which makes the service run without problem, at least until the next compilation. Very strange!
From all Agilior Team, a special thanks to António Cruz.
BFC