How to: Impersonate the Original Caller in WCF Calling from Windows Forms

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 Windows Forms application. The article shows you how to configure the WCF service, implement impersonation, and test the service with a sample Windows Forms client.

Contents

  • Objectives
  • Overview
  • Summary of Steps
  • Step 1: Create a Sample WCF Service
  • Step 2: Configure the WCF Service to Use Windows Authentication
  • Step 3: Implement Impersonation in the WCF Service
  • Step 4: Create a Test Client Application
  • Step 5: Add a WCF Service Reference to the Client
  • Step 6: 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 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.

Use the OperationBehavior attribute to impersonate declaratively on specific operations. Use the Impersonate() method in your code to impersonate programmatically.

In order to reduce the 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 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: Implement Impersonation in the WCF Service
  • Step 4: Create a Test Client Application
  • Step 5: Add a WCF Service Reference to the Client
  • Step 6: Test the Client and WCF Service

Step 1: Create a Sample WCF Service

In this step, you create a WCF service in Visual Studio, hosted in an Internet Information Services (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: 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 4: Create a Test Client Application

In this step, you create a Windows Forms application that you will use to test the WCF service.

  1. Right-click your solution, click Add, and then click New Project.
  2. In the Add New Project dialog box, in the Templates section, select Windows Forms Application.
  3. In the Name field, type Test Client and then click OK to create a Windows Forms application for testing.

Step 5: 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 select Add Service Reference.

  2. In the Add Service Reference dialog box, set the URL to your WCF service: https://localhost/WCFServiceImpersonation/Service.svc

  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 6: Test the Client and WCF Service

In this step, you access the WCF service and make sure that it impersonates as expected.

  1. In your client project, drag a button control onto your form.

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

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

    private void button1_Click(object sender, EventArgs e)
    {
          WCFTestService.ServiceClient myService = new
                                WCFTestService.ServiceClient();
          MessageBox.Show(myService.GetData(123));
          myService.Close();
    }
    
  4. Right-click the client project and select Set as Startup Project.

  5. Run the client application by pressing F5 or CTRL+F5. When you click the button on the form, it should display the message “Hi, <<logged in user id>>, you have entered: 123”.

Note

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 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 all operations

Perform the following steps to impersonate 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 Add.

  4. In the Adding Behavior Extension Element Sections dialog box, choose serviceAuthorization and then click the 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);
     }   
    }
    

Note

It is important to revert to impersonation. Failure to do so can form the basis for denial of service (DoS) and elevation of privilege attacks. In the example above, the using statement ensures that the impersonation is reverted after execution of the using block.

Additional Resources