As I reminder, I discussed beforehand how I had a self-hosted WCF application that use Windows Security as the Authorisation mechanism. When I deployed this to a remote server, clients could not authenticate correctly unless they had the SPN set in the client code:
var channelFactory = new ChannelFactory<IVolumeService>( binding2, new EndpointAddress(new Uri(url), new SpnEndpointIdentity("MYSERVICE/MyMachine") ));
Without this setting they would return a 401 Unauthorised, with the detail:
The HTTP request is unauthorized with client authentication scheme 'Negotiate'.
The authentication header received from the server was 'Negotiate oW8wbaADCgEBom
YEZGBiBgkqhkiG9xIBAgIDAH5TMFGgAwIBBaEDAgEepBEYDzIwMTIwNjI5MTY1NjQ1WqUFAgMOYYqmAw
IBKakRGw9HQVpQUk9NVUsuSU5UUkGqEzARoAMCAQGhCjAIGwZhcG90dHM='.
The remote server returned an error: (401) Unauthorized.
The target principal name is incorrect
Adding the SpnEndpointIdentity allowed the clients to work with this remote server.
However, the twist was this: when I went back to working locally, with a local client and a local server running under my current user (with admin privileges) account - it no longer worked!
This time I would get
System.Net.WebException: The remote server returned an error: (401) Unauthorized
.
at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpCha
nnelRequest.WaitForReply(TimeSpan timeout)
Note there was no The target principal name is incorrect message. It took me a while to figure it, but in the end REMOVING
new SpnEndpointIdentity("MYSERVICE/MyMachine")
got it to work locally! Bah! More reading on SPNs is required.
....
Double Bah! I figured it. It was because I had left a bit of code on the server
var endpointAddress = new EndpointAddress( new Uri(ConfigurationManager.AppSettings["WebServiceUrl"]), EndpointIdentity.CreateSpnIdentity("HOST/servername.domainname:9011"));
and clearly the SpnIdentity in this mismatched the client. Setting BOTH to be the same caused it to work again.
No, this still didn't work. The solution was to use a UPN rather than a SPN, because the account was running under a user account and not localnetwork or system. Both server and client used:
No, this still didn't work. The solution was to use a UPN rather than a SPN, because the account was running under a user account and not localnetwork or system. Both server and client used:
var endpointAddress = new EndpointAddress( new Uri(ConfigurationManager.AppSettings["WebServiceUrl"]), EndpointIdentity.CreateUpnIdentity(WindowsIdentity.GetCurrent().Name));
A hint was also shown by looking at the WSDL of an existing endpoint that ran under a user account:
<Identity xmlns="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
<Upn>MYUSER@mydomain.intra</Upn>
</Identity>
Lessons learnt:
- Self hosting WCF services is more painful than hosting them in IIS.
- Try and use the configuration approach as much as possible - there are more examples out there.
- If you don't publish a metadata exchange you have to bind manually, and this provides more chances to mismatch.
- Programmatically defining and consuming the endpoints is less well documented - and self hosting with Windows Authentication and SPNs is sparsely documented!
- Generating your client from the metadata exposed from the server is much safer.
References:
http://msdn.microsoft.com/en-us/library/ms733130.aspx
http://msdn.microsoft.com/en-us/library/ms677601%28v=vs.85%29.aspx
http://social.msdn.microsoft.com/forums/en-US/wcf/thread/78638457-ca7a-4f88-b8a9-9bc32d4b5c7d/
http://msdn.microsoft.com/en-us/library/bb628618.aspx
No comments:
Post a Comment