0
votes

We have a ASP.NET MVC application running on .NET 4.5.2 which is hosted on IIS 7.5 on Windows 10 or Server 2012R2.

How can I let users sign in using their Windows accounts (using Windows Authentication Mode) and impersonate them so that the service account (configured on the App Pool) is passed through to a WCF Service instead of the original user for back-end authentication/authorization?

1

1 Answers

1
votes

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.