11

Ok, I've read every thread & question I can find with this error and surprisingly have not found a solution. I'm trying to require Windows authentication on my IIS hosted WCF service (.NET 4.0) which, until now, has been optional. I have had a Windows authentication enabled endpoint available on the server for a while with several remote applications successfully using it. I'm now trying to switch our web applications and other server apps that use the WCF service over to this secured endpoint by giving them the exact same client configuration as the working remote clients, but the server apps are receiving a 401 with the message:

The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'.]

I have Anonymous and Windows authentication enabled for the WCF hosting site. The web application I've started with is hosted on a different server than the WCF service and is running on ASP.NET 2.0 and Windows Server 2008 R2 Enterprise. I have both created a client behavior with allowNtlm and set the NetworkSecurity: LAN Manager authentication level to Send LM & NTLM... on the client end. On the hosting end, it is set to Send NTLMv2 Response Only...I don't know if that affects how the server/service handles authentication. I've also tried setting allowedImpersonationLevel to Impersonation on the client which, thankfully, didn't work (because impersonation shouldn't be necessary). We seem to get the same result for a Windows service and console app running on the same server as the web app.

Here is my server config:

<binding name="WindowsSecuredBinding">
    <security mode="Transport">
        <transport clientCredentialType="Windows" />
    </security>
</binding>
...
<service behaviorConfiguration="OMWebServices.QueueServiceBehavior"
    name="OMWebServices.QueueService">
    <endpoint address="" binding="basicHttpBinding" name="QueueEndpoint"
      bindingName="" contract="OMWebServices.IQueueService" />
    <endpoint binding="basicHttpBinding" bindingConfiguration="WindowsSecuredBinding"
      name="QueueSecuredEndpoint" contract="OMWebServices.IQueueService" />
    <endpoint address="mex" binding="mexHttpBinding" name="QueueMetadataEndpoint"
      contract="IMetadataExchange" />
  </service>
...
<behavior name="OMWebServices.QueueServiceBehavior">
    <serviceMetadata httpGetEnabled="true" />
    <serviceDebug includeExceptionDetailInFaults="true" />
</behavior>

And here is the client config:

<endpoint address="https://.../QueueService.svc" binding="basicHttpBinding" bindingConfiguration="QueueSecuredEndpoint" behaviorConfiguration="OMServiceBehavior" contract="OMQueueService.IQueueService" name="QueueSecuredEndpoint" />

<binding name="QueueSecuredEndpoint" 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="Transport">
    <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
    <message clientCredentialType="UserName" algorithmSuite="Default" />
  </security>
</binding>
....
<!-- The behavior I tried that didn't make a difference -->
<behavior name="OMServiceBehavior">
  <clientCredentials>
    <windows allowedImpersonationLevel="Impersonation" allowNtlm="True"/>
  </clientCredentials>
</behavior>

My first question is, what is this error message really telling me? It says the client scheme is Negotiate and the server's responding with Negotiate,NTLM. If the server offers Negotiate and and client is using Negotiate, what's the problem?

Second question is, obviously, what's wrong and how do I make it work?

EDIT
Well this is stupid. The problem seems to be there are no credentials being passed. Way back when the web site was in development, I started writing code to explicitly set the credentials in code, but in the process, found that it was already working without explicitly setting them. So that code has remained commented out. This was running on IIS 6. Now running on IIS 7, it seems to only work if I explicitly set the credentials in my code. Can I get it automatically using the w3wp process' account?

Community
  • 1
  • 1
xr280xr
  • 12,621
  • 7
  • 81
  • 125

5 Answers5

10

To answer the first question, the error message is telling me exactly what it says; I'm not authorized. The line telling me the client authentication scheme and server header is just extra info, not an indication of a conflict. It's actually confirmation that the configuration is correct.

In the staging environment, the problem is being masked because the WCF service and the web application are hosted on the same server. The problem is the web app's site is configured to use IUSR (or IUSR_Server), a local account, for anonymous users by default. This is the user that is being passed (which I believe is equal to CredentialCache.DefaultNetworkCredentials). When they're on different servers, WCF on server 2 obviously can't authenticate a server 1 user. The solution is in IIS, right click Anonymous Authentication > Edit...> check Application pool identity (which is a domain account in my case) or enter a domain account for Specific user.

xr280xr
  • 12,621
  • 7
  • 81
  • 125
3

My problem with this error was not config related but specific to a WCF Service calling another on the same machine.

Because this affected a fleet of new servers that were partly provisioned through a C# console app, I solved it by executing code like this through the affected servers:

const string userRoot = "HKEY_LOCAL_MACHINE";
const string subkey = @"SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0";
const string keyName = userRoot + @"\" + subkey;
Registry.SetValue(keyName, "BackConnectionHostNames", hostnamesOnServer.ToArray(), RegistryValueKind.MultiString);

Reboot wasn't required on Windows Server 2012.

fiat
  • 15,501
  • 9
  • 81
  • 103
2

It just means your client and server are using different authentication scheme.

In your client config, you've set up a

<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />

and a message security

<message clientCredentialType="UserName" algorithmSuite="Default" />

So you might be getting errors because of this. These links might help you.

Also, in your client config

<endpoint address="https://.../QueueService.svc" binding="basicHttpBinding" bindingConfiguration="QueueSecuredEndpoint" behaviorConfiguration="OMSServiceBehavior" contract="OMQueueService.IQueueService" name="QueueSecuredEndpoint" />

Change the behaviorConfiguration from behaviorConfiguration="OMSServiceBehavior" to behaviorConfiguration="OMWebServices.QueueServiceBehavior"

Also did you try to use TransportCredentialOnly? If not, it might be good to try this http://msdn.microsoft.com/en-us/library/ff648505.aspx

<security mode="TransportCredentialOnly">
    <transport clientCredentialType="Windows" />
</security>
minerva
  • 436
  • 6
  • 16
  • Thanks minerva. But it seems to be telling me the authentication schemes are the same; Negotiate. I the behavior spelling was a thread typo, good catch. I'm not using the behavior anymore anyway. For the config, I believe that is the default value for that property anyway (except None instead of Basic256) it was there because VS generated it. I removed it and got the same result. I just tried TransportCredentialOnly and it works, but I want Transport in order to secure all communication. What does it mean that that works? Both use SSL and both use the same credentials, don't they? – xr280xr Mar 18 '14 at 20:42
  • @drpcken provided an explanation of `Transport` and `TransportCredentialsOnly.` Here's the [link](http://stackoverflow.com/a/4481179/1720848) to his answer. – minerva Mar 18 '14 at 23:40
  • @Anand also provide an [explanation](http://stackoverflow.com/a/11502989/1720848) of Transport and TransportCredentialOnly – minerva Mar 19 '14 at 00:08
  • Also noticed that the client webconfig is missing an s `contract="OMQueueService.IQueueService"` should be `contract="OMWebServices.IQueueService"` – minerva Mar 19 '14 at 00:38
  • This might be the most comprehensive [one](http://wcfsecurityguide.codeplex.com/wikipage?title=Ch%2007%20-%20Message%20and%20Transport%20Security%20in%20WCF) that might help you. If you're doing internet & transport, you'll need to install a SSL certificate on IIS. – minerva Mar 19 '14 at 00:52
  • Ah, I was misremembering what TransportCredentialsOnly did since I don't use it. Verified the SSL cert is installed. I don't know why, but the contract is correct as written (generated that way by svcutil). I think the fact that calling the service with TransportCredentialsOnly works proves that. – xr280xr Mar 19 '14 at 15:11
0

This can also apparently be an issue with your credentials not being passed correctly. I needed:

Client.ClientCredentials.Windows.ClientCredential.UserName = User;
Client.ClientCredentials.Windows.ClientCredential.Password = Password;

Instead of:

Client.ClientCredentials.UserName.UserName = User;
Client.ClientCredentials.UserName.Password = Password;

(Oddly, the second way worked once in a while for me, but not always.)

mrsperls
  • 1
  • 2
0

I was having exactly the same issue reported here. The AD account being used for credentials had had the password changed. Once I used the new password it started working.

This error is very misleading for an incorrect password situation.