WCF and passing windows credentials - c#

I have a website hosted on ServerA which runs using an App Pool using a special user accout with domain privilages to access our database. In the config file of the website I specify:
<identity impersonate="true" />
I then have a service which is also on ServerA and hosted in a console app programmatically (i.e. no config file) like below.
Uri uri = new Uri("net.tcp://ServerA:9900/Service/");
ServiceHost host = new ServiceHost(typeof(Service1), uri);
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
ServiceEndpoint serviceEndpoint = host.AddServiceEndpoint(typeof(IService1), binding, uri);
EndpointAddress myEndpointAddress = new EndpointAddress(uri, EndpointIdentity.CreateSpnIdentity("MyspnName"));
serviceEndpoint.Address = myEndpointAddress;
host.Open();
When I open a browser on my local machine and go to the website the website tries to connect to the WCF server and returns the error "The request for security token could not be satisfied because authentication failed."
The website uses the following code to connect to the service:
Uri uri = new Uri("net.tcp://ServerA:9900/Service/");
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
EndpointIdentity epid = EndpointIdentity.CreateSpnIdentity("MyspnName");
EndpointAddress endPoint = new EndpointAddress(uri, epid);
//EndpointAddress endPoint = new EndpointAddress(uri);
ChannelFactory<IService1> channel = new ChannelFactory<IService1>(binding, endPoint);
channel.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Delegation;
IService1 service = channel.CreateChannel();
service.PrintMessage("Print this message!");
For PrintMessage, the method I'm calling, I tried [OperationBehavior(Impersonation = ImpersonationOption.Required)] and .. .Allowed .. but the error is the same.
When I run the website locally using LocalHost there is no error and it works perfect. And also when I change identity impersonate="false" in my web.config it runs but my windows credentials don't get passed into the WCF service which is the whole point.
Any ideas what I'm missing? Pls no general links, I've probably already read it!
thanks a lot

If you use Windows authentication, you can grab the identity of the caller in your service code here:
ServiceSecurityContext.Current.WindowsIdentity
This WindowsIdentity contains things like the ".Name" property, the ".Groups" property of all groups the user belongs to, and more.
If the WindowsIdentity should be NULL, then you don't really have Windows authentication happening.
Are you hosting your WCF service in IIS? Which version - IIS7 is the first one to support net.tcp binding.
What if you self-host your service in a console app - does Windows authentication work then? In that case, it would most likely be a IIS7 config issue of sorts.
Marc

I suspect this is because your service account is not trusted for delegation.
It can therefore impersonate the caller for access to local resources, but not for calling out over TCP. Google "Trusted for delegation" for more info.

Related

How to pass default credentials in Windows Authentication

I'm developing UWP application using C#.net and it has WCF service with Windows Authentication enabled. I struggling to pass default NetworkCredential after consume a service call using Add service reference option.
Please find below my examinations.
When I pass correct windows authentication credentials, it is working as expected.
var service = new ServiceReference.Service1Client();
service.ClientCredentials.Windows.ClientCredential =new NetworkCredential("pradeep","****");
var test = await service.GetDataAsync(1);
but, I wanted pass default network credentials while using my service methis
var service = new ServiceReference.Service1Client();
service.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
var test = await service.GetDataAsync(1);
I also tried below option.
service.ClientCredentials.Windows.ClientCredential = (NetworkCredential)CredentialCache.DefaultCredentials;
When I pass default credentials. I'm getting below exception.
The HTTP request is unauthorized with client authentication scheme
'Negotiate'. The authentication header received from the server was
'Negotiate, NTLM'.
I tested same service call with default NetworkCredential in WPF application which is working as expected.
In order to pass the default credentials for the WCF Windows Authentication in UWP by using the System.Net.CredentialCache.DefaultNetworkCredentials, first please make sure that you have added the Enterprise Authentication and Private Networks(Client & Server) capabilities as following:
For the Enterprise Authentication capability, it is because that Windows domain credentials enable a user to log into remote resources using their credentials, and act as if a user provided their user name and password. The enterprise Authentication special capability is typically used in line-of-business apps that connect to servers within an enterprise.
For the Private Networks(Client & Server) capability, it is because that currently in Windows Runtime we can only pass the default credential in the Intranet. For the Internet we have to use the Username and Password as credential.
For more information about the Capabilities, please check:
https://msdn.microsoft.com/en-us/library/windows/apps/hh464936.aspx .
After that please try to use your Computer name or Fully Qualified Computer name instead of the IP address for your WCF Services like this: http://YourComputerName:YourPortNumber/Service1.svc.
At last please use another computer as client to test the WCF Windows Authentication in UWP with the System.Net.CredentialCache.DefaultNetworkCredentials, then it should work fine.
Thanks.

Accessing SelfHosted WCF Services outside a domain

We have WCF services being self-hosted by a Windows Service inside our domain, using NetTCP with the following settings.
// Set Binding Security.
netTcpBinding.Security.Mode = SecurityMode.Transport;
netTcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
netTcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
We now have a requirement to allow people outside the domain to access these Services (as long as they can provide proper domain credentials). Our goal isn't to host the services via IIS, just allow those outside folks into our services. In my testing I was able to connect to a service from outside by "impersonating" the client proxy credentials during the WCF call as such.
proxy.ClientCredentials.Windows.ClientCredential.Domain = "MyDomainName";
proxy.ClientCredentials.Windows.ClientCredential.UserName = "MyUserName";
proxy.ClientCredentials.Windows.ClientCredential.Password = "MyPassword";
My question is: Is this the correct way? Is there a better way? Any advice would be greatly appreciated.
This route is perfectly valid if you need to imperatively (in code, e.g. a credential popup, or read from a configuration file) set the credentials. A more secure option is to use the windows credential cache. Firstly you would set it up to use the cache:
proxy.ChannelFactory.Credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
Next you would set up the credentials in the credential cache. In Windows XP/2003 this is under "Stored Usernames and Passwords," (in the control panel) in Vista/7/2008 this is under "User Account > Credential Manager" (in the control panel).
As said, your way is perfectly valid - the cache is just more secure.

IIS + Windows Authentication + Web service .NET = HTTP Error 401.1 - Unauthorized

i have big problem.I want to use the web services with Windwos Integrated Authentication and no Anonymous Authentication.
I publish my service (asmx page) on the server. So when i try to access it with Network Credential as show below, i receive HTTP Error 401.1 - Unauthorized.
My_WS.WSClass ws= new My_WS.WSClass();
ws.Credentials = new NetworkCredential("username", "password","domain");
ws.PreAuthenticate = true;
ws.callMethod(Parameter);
On IIS 7 i've enabled only Windows authentication, and my application pool Identity is "Network Service".
If i try my service with anonymous authentication it works.
Any suggestion ?
Thanx

SharePoint search web service error (NTLM) when called from HttpHandler

Here is my scenario: I have a SharePoint site I am working on, and it is on one server farm. In this site, I have created an HttpHandler that uses a SharePoint search webservice that is located on a different server. So that looks something like this:
SharePoint Server A, where my site lives
Has a service reference to SharePoint search web service on Server B
Has an http handler that uses the service reference to call the search service
SharePoint Server B, where the search service lives
My code looks like this:
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
QueryServiceSoapClient _queryService = new QueryServiceSoapClient(binding, new EndpointAddress("http://easearch.ea.com/_vti_bin/search.asmx"));
_queryService.ClientCredentials.Windows.AllowNtlm = true;
_queryService.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
_queryService.ClientCredentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
//_queryService.ClientCredentials.Windows.ClientCredential = new NetworkCredential("MyUsername", "MyPassword", "MyDomain"); //This is the only way it seems to work
//NetworkCredential userCredential = CredentialCache.DefaultCredentials.GetCredential(_queryService.Endpoint.ListenUri, "NTLM");
//_queryService.ClientCredentials.Windows.ClientCredential = userCredential;
string status = _queryService.Status();
If I use this code from a console application on my dev box, it works as expected. But when I try to use the same code from my http handler, it gives the error
The HTTP request is unauthorized with
client authentication scheme 'Ntlm'.
The authentication header received
from the server was 'NTLM'.
I've tried a number of different combinations of the code above and the only one that works from my HttpHandler is when I directly provide my credentials. Anyone have any ideas?
Thanks.
NTLM cannot delegate credentials to a remote server.
This is known as the "double hop" issue. http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx
You'll have to configure Kerberos. Basically:
Configure SharePoint to use Kerberos (in "authentication provider" in central administration)
Create a SPN for SharePoint on your application pool account (with command line "setspn")
Create a SPN for the website runnong on server B on the application account running this site
Configure delegation between the 2
Yes, Kerberos is not that easy to put in place...

Domain.GetDomain(...) fails when called from a web service

I have the following code in a class that is called from a web service:
NetworkCredential credentials = new NetworkCredential("user", "password");
connection = new LdapConnection("domain");
connection.Bind(credentials);
DirectoryContext directoryContext =
new DirectoryContext(DirectoryContextType.Domain, "domain");
// This call returns a domain object with unreadable properties
Domain domain = Domain.GetDomain(directoryContext);
If I instantiate the class directly, all is well, I have a valid domain object that I can work with. If I go through the web service, the domain object is created, but most of the properties throw exceptions, e.g.:
'domain.Children' threw an exception of type ActiveDirectoryOperationException
I have impersonation enabled and am explicitly setting the credentials before calling the web service. Examining Thread.CurrentPrincipal.Identity.Name on the web service side shows the username of the credentials I've explicitly set.
If I look at Request.LogonUserIdentity, I have the following properties:
Name: "domain\\username" (is correct)
ImpersonationLevel: Impersonation
IsAnonymous: false
IsAuthenticated: true
AuthenticationType: NTLM
Anonymous access is disabled (enabling it makes no difference), and 'Basic Authentication' and 'Integrated Windows Authentication' are both checked. The web service is running under IIS 5.1 on my development box.
The code that calls the web service, resulting in a failed call to Domain.GetDomain():
MyServiceProxy proxy = new MyServiceProxy ();
CredentialCache credCache = new CredentialCache();
NetworkCredential netCred = new NetworkCredential(user, password, domain);
credCache.Add(new Uri(proxy.Url), "Ntlm", netCred);
proxy.Credentials = credCache;
proxy.MethodCall();
The code that calls directly and succeeds:
MyService myService = new MyService();
myService.MethodCall();
Any ideas why calls to Active Directory would fail when made in the context of a web service? And again, the call doesn't fail per se... it returns a domain object with unreadable properties.
Thanks in advance!
When you do...
NetworkCredential credentials = new NetworkCredential("user", "password");
connection = new LdapConnection("domain");
connection.Bind(credentials);
DirectoryContext directoryContext =
new DirectoryContext(DirectoryContextType.Domain, "domain");
// This call returns a domain object with unreadable properties
Domain domain = Domain.GetDomain(directoryContext);
...you're in fact just creating a (System.DirectoryServices.Protocols).LdapConnection with specific NetworkCredentials and you validate your "user" and "password" credentials against that. But then you don't use the connection-object anymore; instead you create a new entirely unrelated (System.DirectoryServices.ActiveDirectory).DirectoryContext-object.
And because you're not using a constructor where you're explicitly specifying a username and a password the DirectoryContext-object will get using the credentials of the user running the Application Pool (in IIS 6+, in IIS 5.1 the application will, if memory serves me right, always be a/the local system account - IUSR_XXX - which won't be able to access Active Directory because it's not a domain account).
The different credentials that are used when you're running your code in an IIS-environment versus just testing using a console application (where you're running code as the logged in/interactive user) is a common cause of problems in programming Directory Services.
Try using the constructor where you specify a username and password for the DirectoryContext-object.
As far as impersonation is concerned you might have better luck by using this code-snippet...
System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext =
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
//Insert your code that runs under the security context of the authenticating user here.
impersonationContext.Undo();
...taken from KB306158: How to implement impersonation in an ASP.NET application.
In the end, I converted this to a WCF service hosted in IIS, and the impersonation is fine. I'd love to know what the problem was, but I had to move forward, and a WCF service is a better solution overall, anyway. Thanks for the help, though!

Categories