How to: Impersonate the Original Caller in WCF Calling from a Web Application

patterns & practices Developer Center

Applies To

  • Microsoft Windows Communication Foundation (WCF) 3.5
  • Microsoft Visual Studio 2008

Summary

This how-to article shows you how to impersonate the original caller in a WCF service that has been called from a Web application. The article shows you how to configure the WCF service, implement impersonation, and test the service with a sample Web client.

Contents

  • Objectives
  • Overview
  • Summary of Steps
  • Before You Begin
  • Step 1: Create a Sample WCF Service
  • Step 2: Configure the WCF Service to Use Windows Authentication
  • Step 3: Configure the SPN Identity for the WCF Service Endpoint
  • Step 4: Implement Impersonation in the WCF Service
  • Step 5: Create a Web Application Test Client
  • Step 6: Add a WCF Service Reference to the Client
  • Step 7: Impersonate the Original Caller When Calling the WCF Service
  • Step 8: Configure the Web Application for Constrained Delegation
  • Step 9: Test the Client and WCF Service
  • Additional Information
  • Additional Resources

Objectives

  • Learn how to impersonate the original caller declaratively.
  • Learn how to impersonate the original caller programmatically.
  • Learn how to impersonate for specific WCF operations.
  • Learn how to impersonate for all WCF operations.

Overview

WCF service code can make calls by using the security identity of the service (usually the host process identity or the identity of a service account), or by using the security identity of the original caller. The original caller may be an ASP.NET service account, or it may be the end user of the client application. You impersonate the original caller whenever downstream code needs to authorize based on the original caller's identity. For instance, you may have authorization checks in business logic called by WCF, or you may want to access resources that have access control lists (ACLs) allowing specific user access.

You can impersonate the original caller either declaratively or programmatically, depending on the following circumstances:

  • Impersonate the original caller declaratively when you want to access Microsoft Windows resources that are protected with ACLs configured for your application's domain user accounts.
  • Impersonate the original caller programmatically when you want to access resources predominantly by using the application's process identity, but specific sections of the operation need to use the original caller's identity.

Configure WCF to run using the identity of a lower-privilege account, such as the Network Service account, when it is not impersonating. Use the OperationBehavior attribute to impersonate declaratively on specific operations. Use the Impersonate() method in your code to impersonate programmatically.

In order to reduce attack surface, it is more secure to impersonate only on those operations in which it is necessary to do so. If you do want to impersonate on all operations, set the ImpersonateCallerForAllOperations attribute of ServiceAuthorizationBehavior to True in your application's configuration file.

Summary of Steps

  • Step 1: Create a Sample WCF Service
  • Step 2: Configure the WCF Service to Use Windows Authentication
  • Step 3: Configure the SPN Identity for the WCF Service Endpoint
  • Step 4: Implement Impersonation in the WCF Service
  • Step 5: Create a Web Application Test Client
  • Step 6: Add a WCF Service Reference to the Client
  • Step 7: Impersonate the Original Caller When Calling the WCF Service
  • Step 8: Configure the Web Application for Constrained Delegation
  • Step 9: Test the Client and WCF Service

Before You Begin

Before you can configure WCF to impersonate the original caller from a Web application, you must ensure that you have the following prerequisites in place:

  • You must have Visual Studio 2008 installed.
  • You must have Internet Information Services (IIS) installed and running.
  • You must be in a Microsoft Active Directory environment.
  • You must have access to your Active Directory domain controller.

Step 1: Create a Sample WCF Service

In this step, you create a WCF service in Visual Studio, hosted in an IIS virtual directory.

  1. In Visual Studio, select File > New Web Site.
  2. In the Templates section, select WCF Service. Make sure that the Location is set to Http and specify the virtual directory to be created in the Path (e.g., https://localhost/WCFServiceImpersonation).
  3. In the New Web Site dialog box, click OK to create a virtual directory, a solution file, and a sample WCF service for the solution.
  4. In Microsoft Internet Explorer, browse to your WCF service at https://localhost/WCFServiceImpersonation/Service.svc. You should see details of your WCF service in the browser.

Step 2: Configure the WCF Service to Use Windows Authentication

By default, Visual Studio configures your WCF service to use wsHttpBinding with Windows authentication and message security.

  • In Visual Studio, verify your configuration settings in Web.config. The configuration should look as follows:

Step 3: Configure the SPN Identity for the WCF Service Endpoint

In this step, you configure the service principle name (SPN) identity under which the WCF service will run. This identity is usually the lower-privilege Network Service account. Use of this account will reduce the attack surface when your application is not impersonating.

  1. Right-click the Web.config file and then and select the Edit WCF Configuration option.

    If you do not see the Edit WCF Configuration option, click the Tools menu and select WCF Service Configuration Editor. Close the WCF Service Configuration Editor tool that appears. The option should now appear on the web.config context menu.

  2. Expand the Services node, expand the Service node, and then expand the Endpoints node.

  3. Select the first endpoint and verify that it is configured to use wsHttpBinding.

  4. Select the Identity tab and delete the Dns attribute value, which by default is set to “localhost”.

  5. Set the ServicePrincipalName attribute to “HOST/YourMachineName”. This value depends on the identity that is used for running the WCF service. By default, the WCF service runs under the Network Service identity and is identified by the machine account in the network, hence you can use your machine name.

    Note

    If WCF was running under a domain account, which will be the case in a real-world production scenario, you will have to create a SPN for that identity and set the ServicePrincipalName attribute to the SPN appropriately.

  6. In the Configuration Editor dialog box, on the File menu, click Save.

  7. In Visual Studio, verify your configuration settings in Web.config. The configuration should look as follows:

    ...
    <services>
      <service name="Service" behaviorConfiguration="ServiceBehavior">
        <!-- Service Endpoints -->
        <endpoint address="" binding="wsHttpBinding" contract="IService">
          <identity>
            <servicePrincipalName value="HOST/YourMachineName" />
            <dns value="" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    ...
    

Step 4: Implement Impersonation in the WCF Service

Perform the following steps to declaratively impersonate specific operations:

  1. In the Solution Explorer, expand the App_Code folder under your WCF Service project, and then open the Service.cs file.

  2. Add a using statement for the System.Security.Principal namespace.

  3. Set the impersonation required on the operation implementation of the specific operation as follows:

    [OperationBehavior(Impersonation = ImpersonationOption.Required)]
    public string GetData(int value)
    {
      return string.Format("Hi, {0}, you have entered: {1}",
                               WindowsIdentity.GetCurrent().Name, value);
    }
    

Step 5: Create a Web Application Test Client

In this step, you create a Web application that you will use to test the WCF service. In order to more closely emulate a production scenario, you should create the Web application on a separate physical machine.

  1. In Visual Studio, select File > New Web Site.
  2. In the Templates section, select ASP.NET Web Site. Make sure that the Location is set to Http and specify the virtual directory to be created in the Path (e.g., https://localhost/TestClientWebSite).
  3. In the New Web Site dialog box, click OK to create a virtual directory and a sample ASP.NET Web site.
  4. Open Internet Information Services (IIS) Manager by running the inetmgr command from the command line.
  5. Expand the Default Website node, right-click the new TestClientWebSite virtual directory, and then select Properties.
  6. In the Properties dialog box, click the Directory Security tab.
  7. In the Anonymous access and authentication control section, click Edit.
  8. In the Authentication Methods dialog box, clear the Anonymous access check box, and then select the Integrated Windows authentication check box.
  9. In the Authentication Methods dialog box, click OK.
  10. In the Properties dialog box, click Apply and then click OK.
  11. Run the iisreset command from the command line.

Step 6: Add a WCF Service Reference to the Client

In this step, you add a reference to your WCF service.

  1. Right-click your client project and then click Add Service Reference.

  2. In the Add Service Reference dialog box, set the URL to your WCF service (for example, https://localhost/WCFServiceImpersonation/Service.svc) and then click Go.

  3. In the Namespace field, change “ServiceReference1” to “WCFTestService”.

  4. Click OK.

    A reference to WCFTestService should appear beneath Service References in your client project.

Step 7: Impersonate the Original Caller When Calling the WCF Service

In this step, you impersonate the original caller from the Web application and then call the WCF service.

  1. View the designer for Default.aspx in your Web application.

  2. Drag a button control into the designer.

  3. Double-click the button control to show the underlying code.

  4. Add a using statement for the System.Security.Principle namespace.

  5. Use the Impersonate() method to impersonate the original caller.

  6. Create an instance of the proxy and then call the GetData method of your WCF service. The code should look as follows:

    using System.Security.Principal;
    …
    protected void Button1_Click(object sender, EventArgs e)
    {
        // Obtain the authenticated user's Identity and impersonate the original caller
        using (((WindowsIdentity)HttpContext.Current.User.Identity).Impersonate())
        {
            WCFTestService.ServiceClient myService = new WCFTestService.ServiceClient();
            Response.Write(myService.GetData(123) + "<br/>");
            myService.Close();
        }
    }
    …
    

Step 8 Configure the Web Application for Constrained Delegation

In this step, you configure Active Directory to allow your Web application to use constrained delegation to access a remote WCF service. Constrained delegation allows the Web application to pass the identity of the original user to the WCF service.

If your ASP.NET application runs using the Network Service machine account, you must enable constrained delegation for your Web server computer. However, if your ASP.NET application runs under a custom domain account, you must enable protocol transition and constrained delegation for the custom domain account.

This how-to article assumes that you are running your Web application under the Network Service machine account.

  1. Start the Microsoft Management Console (MMC) Active Directory Users and Computers snap-in.

  2. In the left pane of the MMC snap-in, click the Computers node.

  3. In the right pane, double-click your Web server computer to display the Properties dialog box.

  4. On the Delegation tab of the Properties window for the Web server computer, Do not trust the computer for delegation is selected by default. To use constrained delegation, select Trust this computer for delegation to specified services only.

    You specify precisely which service or services can be accessed in the bottom pane.

  5. Beneath Trust this computer for delegation to specified services only, select Use Kerberos only.

  6. Click Add.

    The Add Services dialog box appears.

  7. Click Users or computers.

  8. In the Select Users or Computers dialog box, type the name of your WCF service computer if you are running using Network Service. Alternatively, if you are running WCF by using a custom domain account, enter that account name instead. Click OK.

    You will see all the SPNs configured for the selected user or computer account.

  9. To restrict access to the WCF service, select the HOST service, and then click OK.

For more information on constrained delegation, see How To: Use Protocol Transition and Constrained Delegation in ASP.NET 2.0.

Step 9: Test the Client and WCF Service

In this step, you access the WCF service via the ASP.NET Web site and ensure that it impersonates as expected.

  1. Rebuild both your WCF Service and Web Application projects.
  2. From the client machine, access the Web application and click the button.
  3. The browser should display the message “Hi, <<logged in user id>>, you have entered: 123”.

Notice that if you remove impersonation from your service and run the client again, the user ID changes from your identity to the ASP.NET identity.

Additional Information

There are two options for impersonation:

  • Impersonating the original caller declaratively
  • Impersonating the original caller programmatically

This how-to article showed how to impersonate specific operations declaratively because this is the most common and secure mechanism for impersonation. The following sections detail the complete set of options available for impersonation.

Impersonating the original caller declaratively

You can impersonate declaratively by applying the OperationBehaviorAttribute attribute on any operation that requires client impersonation. You can impersonate for all operations in the service, or limit the scope to specific operations. Impersonating all operations may increase the attack surface and negatively impact the security of your application.

Impersonating for specific operations

Perform the following steps to impersonate for specific operations:

  1. In the Solution Explorer, expand the App_Code folder under your WCF Service project, and then open the Service.cs file.

  2. Add a using statement for the System.Security.Principal namespace.

  3. Set the impersonation required on the operation implementation of the specific operation as follows:

    [OperationBehavior(Impersonation = ImpersonationOption.Required)]
    public string GetData(int value)
    {
      return string.Format("Hi, {0}, you have entered: {1}",
                               WindowsIdentity.GetCurrent().Name, value);
    }
    

Impersonating for all operations

Perform the following steps to impersonate for all operations:

  1. Right-click the Web.config file and then select the Edit WCF Configuration option.

  2. Expand the Advanced node and then expand the Service Behaviors node.

  3. Select the ServiceBehavior service behavior, and then click the Add button.

  4. In the Adding Behavior Extension Element Sections dialog box, choose serviceAuthorization and then click Add.

  5. Select the serviceAuthorization node and then set the ImpersonateCallerForAllOperations attribute to True.

  6. In the Configuration Editor dialog box, on the File menu, click Save.

  7. In Visual Studio, verify your configuration settings in Web.config. The configuration should look as follows:

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

    Note

    When impersonating for all operations, the Impersonation property of the OperationBehaviorAttribute applied to each method must also be set to either Allowed or Required.

Impersonating the original caller programmatically

Perform the following steps to impersonate the original caller programmatically:

  1. In the Solution Explorer, expand the App_Code folder under your WCF Service project, and then open the Service.cs file.

  2. Add a using statement for the System.Security.Principal namespace.

  3. Use the Impersonate() call to impersonate the original caller, and then use GetCurrent() to revert back to the previous state, as follows:

    public string GetData(int value)
    {
     using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
     {
         // return the impersonated user (original users identity)
         return string.Format("Hi, {0}, you have entered: {1}",
              WindowsIdentity.GetCurrent().Name, value);
     }   
    }
    

Additional Resources