After some time I managed to come across an answer and solution that works.
Initially it seemed that IIS can only pass one user to the application. If you open up the Configuration Editor for the MVC app and open the system.webServer/serverRuntime
section, you will see authenticatedUserOverride
has UseAuthenticatedUser
and UserWorkerProcessUser
. This will determine which user is sent to MVC and you will find the WindowsIdentity
under the User.Identity
Properties in a Controller in the MVC app. This will not work.
I stumbled upon an article from Microsoft (How to: Impersonate the Original Caller in WCF Calling from a Web Application) which gave me the basis to work from which lead to the solution.
In the article they make mention that you need to make use of Impersonate()
calls which looks something like this which is used on the client side:
using (((WindowsIdentity)User.Identity).Impersonate())
{
var channelFactory = new ChannelFactory<IService1>("*");
var proxy = channelFactory.CreateChannel();
return View((object)proxy.GetName("1"));
}
On the service side you need to mark your operations for Impersonation:
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
I also added the PrincipalPermission
to make sure that only the service account comes through:
[PrincipalPermission(SecurityAction.Demand, Name = "PC-NAME\\SvcAccount")]
And it seems that you need to make use of the wsHttpBinding
binding.
<wsHttpBinding>
<binding name="secure">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Windows" />
</security>
</binding>
</wsHttpBinding>
I got it to work over SSL and it passed credentials through automatically to the WCF service but it was the original user instead of the service account, so I got authorization errors coming from the service.
Upon further googling I found this snippet which gave me an idea: Getting NetworkCredential for current user (C#)
I substituted my client code to look like this:
using (System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate())
{
var channelFactory = new ChannelFactory<IService1>("*");
var proxy = channelFactory.CreateChannel();
return View((object)proxy.GetName("1"));
}
Then lo and behold! It worked! Service authorized the user (I changed the name in the attribute just to make sure I wasn't dreaming) and returned a value and the front-end UI still showed the logged in user.