2

I have a WCF service which uses Windows Authentication to view Service Contract and a specfic method in a service is configured to be accessed only by a specific user UserX.

[PrincipalPermission(SecurityAction.Demand,Name="xxx\\UserA")]

In the client side, I need to access the above service method. If I am using a Web Reference -> I add the following

client = new WebRefLocal.Service1();
client.Credentials = new System.Net.NetworkCredential("UserA", "xxxxxx", "test");

But the above cannot be achieved in WCF Service Reference as Client Credentials are read-only. One best way I can achieve the above is Impersonation https://msdn.microsoft.com/en-us/library/ff649252.aspx.

My question here is

  1. Why ClientCredentials are made readonly in WCF?
  2. How Network Credential work? Will they authenticate the Windows login in client side or server side?
  3. Is there is any way I can achieve the above in WCF aswell without impersonation?
Joe 89
  • 850
  • 1
  • 12
  • 30
  • 1
    Have a look at claims based authentication – 3dd Sep 02 '15 at 07:20
  • @MickyDuncan been down the impersonation route with WCF, it get's really hairy once you run into the double hop problem. Using a claims based system eases the pain – 3dd Sep 02 '15 at 07:31
  • possible duplicate of [How to specify Windows credentials in WCF client configuration file](http://stackoverflow.com/questions/950928/how-to-specify-windows-credentials-in-wcf-client-configuration-file). Though it refers to "config file", the solutions are code-based –  Sep 02 '15 at 07:31
  • 1
    @3dd Ahh You are referring to point 3. Yes completely agree with you there good buddy! I'm still in therapy from my DCOM days –  Sep 02 '15 at 07:33
  • Even the code based is not possible under WCF, as ClientCredentials are readonly we cannot assign any new credential to the service client. Thats why I was looking into impersonation but felt its too complicated to deal with. – Joe 89 Sep 02 '15 at 08:13
  • @MohanPrasath Well no, you assign the new credentials to the existing `ClientCredentials` - **there is no need for a different object**. As per the code from that link: `Svc.ClientCredentials.UserName.UserName = AppSettings["WCFSvcUsername"];` –  Sep 02 '15 at 08:33
  • @Micky Duncan: I believe the above depends on the binding that we use.. I am using BasicHttpBinding and when I use the above it is taking only the account in which I am executing the application rather than the one specified in the UserName and Password.. – Joe 89 Sep 02 '15 at 09:38
  • @MohanPrasath don't use `BasicHttpBinding` when using passwords/usernames as it's being sent in clear text, if I'm not mistaken WCF will stop you from assigning the values when using `BasicHttpBinding` as it knows that it's unsecure. Unless your using `TransportSecurity` – 3dd Sep 02 '15 at 10:03

2 Answers2

2

I've done something like this - hope it helps:

 var credentials = new ClientCredentials();
credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Delegation;
credentials.Windows.ClientCredential = new System.Net.NetworkCredential("UserA", "xxxxxx", "test");

client.Endpoint.Behaviors.Remove<ClientCredentials>();
client.Endpoint.Behaviors.Add(credentials);

Used with a BasicHttpBinding with following security settings:

  <security mode="TransportCredentialOnly">
    <transport clientCredentialType="Windows" proxyCredentialType="Windows" />
  </security>
Daniel Stackenland
  • 3,149
  • 1
  • 19
  • 22
  • Can you share what is the binding that you used for the above?? – Joe 89 Sep 02 '15 at 09:33
  • Thanks.. This gave me the exact solution.. I did the following service.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("xxx", "yyyy", "zzzz"); ServRef.Resultresult = service.GetData(); I didn't use the Impersonation also..:) – Joe 89 Sep 02 '15 at 11:17
0

One method you can use is to make use of ChannelFactory when calling the WCF service.

The following code was taken from one of my MVCprojects, hence it has someModelState` validation code, I'm sure you can modify it to suit your needs.

protected R ExecuteServiceMethod<I, R>(Func<I, R> serviceCall) {
    R result = default(R);
    ChannelFactory<I> factory = CreateChannelFactory<I>();
    try {
        I manager = factory.CreateChannel();
        result = serviceCall.Invoke(manager);
    } catch (FaultException<ValidationFaultException> faultException) {
        faultException.Detail.ValidationErrors.ToList().ForEach(e => ModelState.AddModelError("", e));
    } finally {
        if (factory.State != CommunicationState.Faulted) factory.Close();
    }
    return result;
}

private ChannelFactory<I> CreateChannelFactory<I>() {
   UserAuthentication user = GetCurrentUserAuthentication();

   ChannelFactory<I> factory = new ChannelFactory<I>("Manager");

   if (IsAuthenticated) {
       factory.Credentials.UserName.UserName = user.UserName;
       factory.Credentials.UserName.Password = user.Password;
   }

   BindingElementCollection elements = factory.Endpoint.Binding.CreateBindingElements();
   factory.Endpoint.Binding = new CustomBinding(elements);
   SetDataContractSerializerBehavior(factory.Endpoint.Contract);

   return factory;
}
3dd
  • 2,520
  • 13
  • 20