REST, Windows Authentication and Service Principal Name

Topics: Web Api
Jul 1, 2011 at 1:35 PM

I'm having trouble using the HttpClient for Windows authentication for some Intranet calls to my REST service. I'm configuring the service binding as follows:

var binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;

On the client I'm supplying credentials as follows and this works fine most of the time:

client = new HttpClient(baseUrl);
var channel = new HttpClientChannel();
channel.Credentials = CredentialCache.DefaultCredentials;
client.Channel = channel;

But on some cross machine intranet calls the client receives a 401 Unauthorized error indicating 'The target principal name is incorrect', with the below stack trace. I tend to get different results depending whether I use the machine name, IP address or fully qualified domain name of the host - but there is no consistent behaviour. Service and client machines are on the same Windows domain, and service / client users are domain accounts.

at System.Net.NTAuthentication.GetOutgoingBlob(Byte[] incomingBlob, Boolean throwOnError, SecurityStatus& statusCode)   at System.Net.NTAuthentication.GetOutgoingBlob(String incomingBlob)   at System.Net.NegotiateClient.DoAuthenticate(String challenge, WebRequest webRequest, ICredentials credentials, Boolean preAuthenticate)   at System.Net.NegotiateClient.Authenticate(String challenge, WebRequest webRequest, ICredentials credentials)   at System.Net.AuthenticationManager.Authenticate(String challenge, WebRequest request, ICredentials credentials)   at System.Net.AuthenticationState.AttemptAuthenticate(HttpWebRequest httpWebRequest, ICredentials authInfo)   at System.Net.HttpWebRequest.CheckResubmitForAuth()   at System.Net.HttpWebRequest.CheckResubmit(Exception& e)

With the ChannelFactory I can work around this by setting a Service Principal Name as follows in the endpoint identity. This fixes the problem for all machines, regardless of how I specify the machine name in the address. But I would rather use the new REST stack instead and there doesn't seem to be anything equivalent.

var address = new EndpointAddress(new Uri(baseUrl), new SpnEndpointIdentity(String.Empty));
var factory = new ChannelFactory<IMyService>(binding, address);
factory.Credentials.Windows.ClientCredential = CredentialCache.DefaultCredentials;

Looking at traffic with Fiddler, the standard HTTP challenge / response mechanism takes place, and the client supplies credentials by indicating Authorization=Negotiate in the Request Header. The encoded authorization data is formatted differently for the HttpClient / ChannelFactory. I'm noticing that when authentication fails the Authorization header data provided by the HttpClient is very long.

I'm thinking this might possibly be something like the security negotiation trying to fall back to NTLM rather than using Kerberos. I was wondering if anyone else has resolved this problem with the HttpClient, or knows a workaround to make HttpClient supply the Authorization request in the same manner as ChannelFactory with SpnEndpointIdentity?

Jul 4, 2011 at 10:14 AM
I think I've figured this out in the end from the below article.
http://msdn.microsoft.com/en-us/library/cc982052(v=vs.90).aspx
In the client code I need to set the service principal name using the AuthenticationManager class. 
client.TransportSettings.Credentials = CredentialCache.DefaultCredentials;
AuthenticationManager.CustomTargetNameDictionary[baseUrl] = "HTTP\myloadbalanceddomainname.com";
Using an empty SPN as above means NTLM is used, which is what was happening in my above ChannelFactory code.
So I should set the SPN as follows, to allow Kerberos authentication as well as NTLM.
For a REST client I think the above SPN format is correct, based on this article.
http://archive.msdn.microsoft.com/KB980588/Wiki/View.aspx?title=Home&version=1
Jul 4, 2011 at 10:14 AM
I think I've figured this out in the end from the below article.
http://msdn.microsoft.com/en-us/library/cc982052(v=vs.90).aspx
In the client code I need to set the service principal name using the AuthenticationManager class. 
client.TransportSettings.Credentials = CredentialCache.DefaultCredentials;
AuthenticationManager.CustomTargetNameDictionary[baseUrl] = "HTTP\myloadbalanceddomainname.com";
Using an empty SPN as above means NTLM is used, which is what was happening in my above ChannelFactory code.
So I should set the SPN as follows, to allow Kerberos authentication as well as NTLM.
For a REST client I think the above SPN format is correct, based on this article.
http://archive.msdn.microsoft.com/KB980588/Wiki/View.aspx?title=Home&version=1
Jul 4, 2011 at 10:14 AM
I think I've figured this out in the end from the below article.
http://msdn.microsoft.com/en-us/library/cc982052(v=vs.90).aspx
In the client code I need to set the service principal name using the AuthenticationManager class. 
client.TransportSettings.Credentials = CredentialCache.DefaultCredentials;
AuthenticationManager.CustomTargetNameDictionary[baseUrl] = "HTTP\myloadbalanceddomainname.com";
Using an empty SPN as above means NTLM is used, which is what was happening in my above ChannelFactory code.
So I should set the SPN as follows, to allow Kerberos authentication as well as NTLM.
For a REST client I think the above SPN format is correct, based on this article.
http://archive.msdn.microsoft.com/KB980588/Wiki/View.aspx?title=Home&version=1
Jul 4, 2011 at 6:36 PM

Thank you ... this was exactly my issue and your suggestion worked for me!