Double Hop Windows Authentication with IIS Hosted WCF Service

Hello, Randy Evans here.  I am a principal developer on the Information Security Tools Team.  In a recent project, we had a intranet web site that called an IIS hosted WCF service.  The WCF service, in turn, called a SQL Server Reporting Services (SSRS) web service. We wanted to utilize the authorization mechanisms of SSRS. To do this, we needed to impersonate the end user when we made the calls to SSRS.  For the above scenario to work, we needed to perform an authentication double hop.  Meaning, the web site needed to impersonate the user when calling the WCF service and the WCF service also needed to impersonate the user when calling SSRS.  Our project was using Kerberos based authentication.  By default, Windows authentication does not allow a user’s impersonated credentials to be reused when attempting to authenticate to a remote resource.  The SSRS web service is considered a remote resource from the WCF service.  There are many blogs and listings in social networks that explain how to impersonate a user coming to an IIS hosted WCF service.  However, these articles do not explain how to perform the double hop.  Once the service is set up to accept impersonation, the answer was actually quite simple. 

Windows authentication supports 5 levels of impersonation.  The forth, and default, level is Impersonate. The fifth level is Delegate.  The default level does not allow the double hop authentication. To enable the double hop, the client calling the WCF service needs to set the impersonation level of the WCF service to Delegate.  This is performed with the following code:

   Service1Client service = new Service1Client();

  //Sets the impersonation level to delegation.  Without delegation level impersonation, 
  //the WCF service would not be able to impersonate to a remote server.
  service.ClientCredentials.Windows.AllowedImpersonationLevel = <br>     System.Security.Principal.TokenImpersonationLevel.Delegation; 

  string retString = service.GetData(1);

In addition to setting the impersonation level to Delegate, there are two other places where changes need to occur.

  1. Enable impersonation at the web site.  Either enable ASP.NET impersonation for the entire site or temporarily impersonate the user from within the web site by using WindowsIdentity.Impersonate(token).

    Add the following attribute to each method in the WCF service that needs impersonation.

    [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]

    Example:

      [OperationBehavior(Impersonation = ImpersonationOption.Allowed)] 
    public string GetData(int value)
    {
    
         //This is the service call that requires the double hop.
        ReportingService2005 reportService = new ReportingService2005();
        reportService.Credentials = System.Net.CredentialCache.DefaultCredentials;
    
        //Get a listing of all items in the reporting service catalog that are in the root folder.
        CatalogItem[] catalogItems = reportService.ListChildren("/", false);
    
        StringBuilder retString = new StringBuilder();
    
        foreach (CatalogItem catalogItem in catalogItems)
            retString.Append("<br>" + catalogItem.Name);
    
        retString.Append("<br>You entered: " + value.ToString());
    
        return retString.ToString();
    }
    
     NOTE – ImpersonationOption.Required will work as well.
    
  2. There are three modifications required in the web.config file of the WCF Service web application.

    1. Add the following binding node under the <system.serviceModel> node to enforce Windows authentication.

           <bindings>
            <basicHttpBinding>
              <binding name="winAuthBasicHttpBinding">
                <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Windows"/>
                </security>
              </binding>
            </basicHttpBinding>
          </bindings>
    

         2. Reference this binding from the service endpoint node.

          <endpoint address="" 
                   binding="basicHttpBinding" 
                   bindingConfiguration="winAuthBasicHttpBinding" 
                   contract="Service.Service" /> 

Note that the binding name string must match the bindingConfiguration string. In this example the string value is “winAuthBasicHttpBinding”.

          3. Add the following node to the service’s serviceBehaviors behavior node.

          <serviceAuthorization impersonateCallerForAllOperations="true" />

                  Example:

         <behaviors>
            <serviceBehaviors>
                <behavior name="Service.ServiceBehavior">
                    <serviceMetadata httpGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="True" />
                     <serviceAuthorization impersonateCallerForAllOperations="true" /> 
                </behavior>
            </serviceBehaviors>
        </behaviors>

Follow the below link for more details from MSDN on using impersonation with WCF services.

https://msdn.microsoft.com/en-us/library/ms730088.aspx