Some background:
We're migrating a fairly complex web-serviced architecture to newer Windows servers. Some web sites/services have been moved from IIS6 to IIS7 as part of this. So new machine, same configuration, possibly different version of IIS.
All code has been recompiled (unchanged) into VS2012/.NET 4.0. This is a mix of C# and VB.NET.
We have a WCF web service with the following configuration.
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<extensions>
<behaviorExtensions>
<add name="caller" type="xxx.CallerBehaviorExtensionElement, xxx, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="caller">
<caller />
</behavior>
<clientCredentials>
<serviceCertificate>
<authentication revocationMode="NoCheck" />
</serviceCertificate>
<windows allowedImpersonationLevel="Delegation" />
</clientCredentials>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="xxxWcfBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="xxxWcfBehavior" name="xxxWcf">
<endpoint address="" behaviorConfiguration="caller" binding="wsHttpBinding" contract="xxxWcf">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
Calling this from ASMX web service (1) works fine. Calling this from ASMX web service (2) does not work, returning the following exception:
System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---> System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
This is the client's web.config:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="caller" type="xxxx.CallerBehaviorExtensionElement, xxx, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="caller">
<caller />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="xxxDataWcf" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None" realm=""/>
<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://xxxDataWcf.svc"
binding="wsHttpBinding" bindingConfiguration="xxxDataWcf"
contract="xxxDataWcf" name="xxxDataWcf">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
But what is the difference between (1) and (2)? I would suggest nothing. Multiple people have compared IIS configurations (this occurs across v6 and v7), compared web.configs, created test projects and compared permissions.
I've been torn whether to include the CallerBehaviorExtensionElement or not. I'm sure that it isn't relevant but have included it because removing it would invalidate the configuration to the point of being unhelpful. All this is is a cheat to pass the username across the hops without relying on delegation/ASP.NET authentication. To be clear, this does not form authorisation nor does it interact with Windows' authorisation infrastructure in any way. We just use it to mark the eventual record with the caller's username.
Based on our previous investigations:
- We're not going cross-domain
- We don't want to reduce security as this is a typical response WCF: The request for security token could not be satisfied because authentication failed
It seems the stock response for the error is to weaken security, which we are loathe to do. This worked fine in an IIS6 configuration and works fine on a local development machine under IIS7 (bizarrely, even across machine/OS boundaries). So it works in principle.
We're hitting our heads and look to the community for a hint. If there is any specific information you need, please ask. This is WCF and I'm sure you'll appreciate that the configuration can be very noisy.