SYSK 373: How to Impersonate the Original Caller When Calling the WCF Service in ASP.NET by Configuration Only

If your WCF services use role based authorization, and you need to pass the identity of the end user (original caller), you have a few options:

 

1. Use <identity impersonate=”true” /> in web.config

 

This is not my preference since there is quite a bit of overhead with doing security context switching for every web request, not to mention that all activity (not just WCF service invocations) will be done in the original caller’s context, e.g. you database calls, file access, etc.

 

2. Call HttpContext.Current.User.Impersonate before calling a WCF service as described in http://msdn.microsoft.com/en-us/library/cc949013.aspx:

 

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();

    }

}

 

 

3. And, finally, my preference – using WCF behavior extensions

 

Here is how it works:

 

1. In the web.config of your web site, add the following behavior extension:

 

<system.serviceModel>

  <extensions>

    <behaviorExtensions>

      < addname = "httpContextIdentity"type="HttpContextIdentityBehavior, WCFExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

    </behaviorExtensions>

  </extensions>

  <behaviors>

    <endpointBehaviors>

        <behavior name="YourBehaviorNameHere">

          . . .

          < httpContextIdentity />

        </behavior>

      </endpointBehaviors>

  </behaviors>

  . . .

</system.serviceModel>

 

2. Create WCFExtensions class library project and add the HttpContextIdentityBehavior implementation class listed below:

 

using System;

using System.Security.Principal;

using System.ServiceModel.Description;

using System.ServiceModel.Dispatcher;

using System.Web;

// TODO: add SEH & logging

public class HttpContextIdentityBehaviorImpl : IClientMessageInspector, IEndpointBehavior

{

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)

    {

        WindowsImpersonationContext wic = correlationState as WindowsImpersonationContext;

        if (wic != null)

        {

            wic.Undo();

            wic.Dispose();

        }

    }

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)

    {

        if (HttpContext.Current != null && HttpContext.Current.User != null)

        {

            WindowsIdentity wi = (WindowsIdentity)HttpContext.Current.User.Identity;

            return wi.Impersonate();

        }

        else

        {

            // TODO: log error

            return null;

        }

    }

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)

    {

        // Nothing to do

    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)

    {

        clientRuntime.MessageInspectors.Add(this);

    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

    {

        // HACK: in .NET 3.5 on Windows 7, this event is not fired...

        // As a workaround, use MessageInspectors instead of CallContextInitializers

        //foreach (DispatchOperation od in endpointDispatcher.DispatchRuntime.Operations)

        //{

        // od.CallContextInitializers.Add(new X());

        //}

    }

    public void Validate(ServiceEndpoint endpoint)

    {

        // Nothing to do

    }

}

public class HttpContextIdentityBehavior : System.ServiceModel.Configuration.BehaviorExtensionElement

   

{

    public override Type BehaviorType

    {

        get { return typeof(HttpContextIdentityBehaviorImpl); }

    }

    protected override object CreateBehavior()

    {

        return new HttpContextIdentityBehaviorImpl();

    }

  

}

 

 

3. Deploy WCFExtensions.dll assembly to the web site’s bin folder (or GAC, or use <probing> element to tell the .NET class loader where to find it…)

 

4. That’s it…

 

 

As always, your comments are welcome and are appreciated.