February 2011

Volume 26 Number 02

Windows Workflow - Securing WF 4 Workflow Services

By Zulfiqar Ahmed | February 2011

Windows Workflow Foundation (WF) provides a visual authoring experience for writing software logic. Once software logic is implemented as a workflow, it’s executed by hosting the workflow in a workflow host. A workflow service is a special type of Web service implemented using a workflow and is made available to the consumer by hosting it in a WorkflowServiceHost. In this article, I’ll talk about security options of the different workflow hosts with a particular focus on the workflow services and the WorkflowServiceHost. I’ll explain some key extensibility points that can be used to extend the workflow services security boundary to the workflow layer. I’ll also discuss the Workflow Security Pack (WFSP) project and how its collection of activities can be used to bring end-to-end security to workflow solutions. WF 4, which is shipped as part of the Microsoft .NET Framework 4, provides an extensible hosting API and comes out-of-the-box with three different hosts with varying capabilities.

WorkflowInvoker This is the most basic and least capable host interface, providing a simple API for invoking workflows. A WorkflowInvoker object only supports a single workflow instance, passed to it via the constructor or the static Invoke method. All workflow execution is guaranteed to be on the same calling thread, so if calling code is impersonating a particular security context, all the activities will execute under this impersonated context. WorkflowInvoker isn’t a Workflow Host in the true sense; rather, it encapsulates a WorkflowApplication-based host and uses a pump-based synchronization context to provide an easy-to-use API with consistent execution semantics. For example, Exceptions, Transactions and so on seamlessly flow across the invocation boundary. This behavior simplifies the security, as the security context of the caller is available throughout the workflow execution and activities can use it in various security scenarios. For example, Principal-Permission authorization and Kerberos-delegation work seamlessly with WorkflowInvoker.

WorkflowApplication This is a slightly more capable host but still supports only a single instance. This host executes workflow using the IO threads from the CLR ThreadPool. The security context of a calling thread isn’t copied to the workflow thread, so even if the workflow client is impersonating, the WF thread—which is executing the activities—won’t be impersonating. The security context of the caller can be flown to the WF thread using a custom Synchronization context that would forward the call on the same incoming Async thread, similar to the Synchronization context used by the WorkflowInvoker:

public class MySyncContext : SynchronizationContext
{
  public override void Post(SendOrPostCallback d, object state)
  {
    d(state);
  }
}

WorkflowServiceHost This is the most comprehensive host, providing a hosting environment suitable for multiple workflow instances. Workflow services are a special type of Web services whose implementation is based on workflows. WorkflowServiceHost derives from the standard Windows Communication Foundation (WCF) ServiceHostBase class, and all the WCF security concepts apply to WorkflowServiceHost as well. Messaging activities are the primary interaction model supported by the WorkflowServiceHost, along with the WorkflowHostingEndpoint, which enables the use of WorkflowServiceHost without using messaging activities. In this article, I’ll primarily focus on the security aspect of messaging activities, Workflow Services and the WorkflowServiceHost. For an overview of workflow services technology, please check out Leon Welicki’s article, “Visual Design of Workflows with WCF and WF 4,” in the May 2010 issue of MSDN Magazine (msdn.microsoft.com/magazine/ff646977).

Workflow Services Wire Security

As workflow services are standard WCF services, wire security aspects are configured using the standard WCF binding mechanisms. A workflow service can be exposed using one or more endpoints using a particular binding as per the security needs of the service. A WCF dispatch pipeline will only execute if the incoming message satisfies the security requirements of the target endpoint. Workflow logic executes at the end of a dispatch pipeline, so all common WCF extensibility points are applicable to workflow services as well. For example, the standard ServiceAuthorizationManager extensibility point can be used to apply authorization for workflow services as well. Frameworks like Windows Identity Foundation (WIF) integrate with WCF at the dispatcher level, and these can transparently be used with workflow services as well. There are some threading differences, related to few async points between the WCF and WF layers, which make certain Thread Local Storage (TLS)-related scenarios a bit more challenging in the workflow services. For example, WIF exposes the incoming identity information using the Thread.CurrentPrincipal and it makes sure to set this correctly for the code-based services. However, your workflow logic might end up executing on a different thread than the original WCF thread. If that happens, all TLS-related data—including the Thread.CurrentPrincipal—won’t be valid, so it’s advised not to rely on TLS in your workflows. I’ll talk about some potential solutions to this in a later section.

WF 4 also provides a Send activity for calling other Web services from within a workflow. The Send activity can be configured with a binding that would be used when calling other services from within the workflow. Internally, the Send activity uses the standard WCF ChannelFactory/Channel API for sending messages, and the configured binding is used to create this internal channel factory. Send activity also has a caching layer built into it that’s used for ChannelFactory/Channel caching. By default, this caching layer is only used if the endpoint information is specified directly using the properties of the Send activity and a stock binding is chosen, as shown in Figure 1.

Figure 1 Send Activity Properties

As soon as endpoint information is loaded from the config file using the EndpointConfigurationName property, the safe caching is disabled and every execution of Send activity creates a brand-new ChannelFactory. Secure bindings such as wsHttpBinding and wsFederationHttpBinding do quite a lot of work at the channel factory opening stage, and recreating a channel factory for each message could be quite expensive. For example, the default WSHttpBinding is optimized for performance and security and achieves this by establishing a secure conversation session that has an upfront cost, but subsequent messages can be secured with a much smaller cost. Without ChannelFactory caching, this optimum behavior of WSHttpBinding becomes an overhead because for every business message, four additional infrastructure messages are sent to negotiate the service credentials and then to establish a secure conversation session, as shown in Figure 2.

Figure 2 Infrastructure Messages Sent to Negotiate Service Credentials

http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue
http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue
http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT
http://tempuri.org/IPingService/Ping
http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT/Cancel

In WF 4, any binding configuration loaded from the configuration file (even the default bindings) is treated as “unsafe for caching” by the Send activity, and the ChannelFactory caching is disabled. This default behavior can be overridden by forcing unsafe channel caching, which will reuse the ChannelFactory for sending messages to the same endpoint. SendMessageChannelCache service behavior enables unsafe caching and also allows the configuration for various Channel and ChannelFactory cache settings, as shown here:

<serviceBehaviors>
  <behavior>
    <sendMessageChannelCache allowUnsafeCaching="true">
      <channelSettings idleTimeout="1:0:0" maxItemsInCache="60"/>
      <factorySettings idleTimeout="1:0:0" maxItemsInCache="60"/>
    </sendMessageChannelCache>
  </behavior>
</serviceBehaviors>

Where Is My OperationContext?

In traditional code-based services, security information for the incoming call is available via the OperationContext.Current. The WCF dispatcher runtime makes sure to set the OperationContext on the thread just before calling the service method, so inside the service method, security information is accessible using the OperationContext.Current.

Workflow services add additional complexity because there are a bunch of async points between the WCF dispatcher and workflow execution. Any one of these async points can switch the thread, in which case workflow logic (activities) would execute on a different thread from the WCF thread and OperationContext wouldn’t be available using the OperationContext.Current approach. In WF 4, thread-agnostic access to OperationContext is enabled using a callback mechanism based on IReceiveMessageCallback and ISendMessageCallback. IReceiveMessageCallbacks are used on the server side, while ISendMessageCallbacks give access to the OperationContext on the client side. On the server side, IReceiveMessageCallbacks are invoked just after a message is received by a Receive activity. To attach these callbacks to Send and Receive activities, they must be available as execution properties when Send/Receive executes. A common approach for adding execution properties to an activity is to create a parent scope activity and set the 
execution properties as part of the parent activity’s execution, as shown in Figure 3.

Figure 3 A Simple Scope Activity to Add Execution Properties

[ContentProperty("Body")]
public sealed class OperationContextScope : NativeActivity
{
  public Activity Body { get; set; }
  protected override void Execute(NativeActivityContext context)
  {
    if (this.Body != null)
    {
      // Adding an execution property to handle OperationContext
      context.Properties.Add(OperationContextScopeProperty.Name,
        new OperationContextScopeProperty());
      context.ScheduleActivity(this.Body);
    }
  }
}

In the code snippet in Figure 3, when the OperationContextScope activity executes, it simply adds an execution property to the context so that all the child activities can see this execution property. Send and Receive activities look for the previously mentioned callback properties, and if one of these properties is found, it’s invoked at the correct stage of message processing, giving you access to the OperationContext. In this example, any Receive activity that would be part of the same scope sees the OperationContextScopeProperty and executes the callback passing in the OperationContext value (see Figure 4).

Figure 4 ReceiveMessageCallback Implemented as an Execution Property

[DataContract]
class OperationContextScopeProperty : IReceiveMessageCallback,  IExecutionProperty
{
  private OperationContext current;
  private OperationContext orignal;
  public static readonly string Name = 
    typeof(OperationContextScopeProperty).FullName;
  public void OnReceiveMessage(OperationContext operationContext, 
    ExecutionProperties activityExecutionProperties)
  {
    current = operationContext;
    operationContext.OperationCompleted 
      += delegate(object sender, EventArgs e)
    {
      current = null;
    };
  }
  public void CleanupWorkflowThread()
  {
    OperationContext.Current = orignal;
  }
  public void SetupWorkflowThread()
  {
    orignal = OperationContext.Current;
    OperationContext.Current = current;
    }
}

OperationContextScopeProperty simply captures and stores the currently active OperationContext and later on sets it on the correct WF thread using the WF TLS mechanism. The IExecutionProperty interface has Setup/CleanUpWorkflowThread methods, which are called before and after executing every WF work item (activity) and give the ability to set various TLS-related properties on the selected WF thread, OperationContext being one example in this case.

OperationContextScope is an example of a custom activity that leverages the WF 4 extensibility to enable thread-agnostic access to the WCF OperationContext for all in-scope child activities.

Workflow Services and WIF

WIF provides a rich API and object model to claim-enable WCF services and ASP.NET applications. WIF integrates with WCF at the host level, so most of the WIF features work with Workflow Services as well. Please check my blog post bit.ly/a6pWgA for additional details on integrating WIF with Workflow Services. The out-of-box integration works fine for basic scenarios, while additional rich scenarios can be enabled using the activities from WFSP.

Introducing WFSP CTP 1

WFSP Community Technology Preview (CTP) 1 provides a collection of activities and associated WCF behavior to enable key security scenarios in WF 4. WFSP leverages the ISend/IReceiveMessageCallback extensibility model to implement many of its features. CTP 1 of WFSP was released on CodePlex in July 2010 and can be downloaded from wf.codeplex.com/releases/view/48114.

WFSP activities, shown in Figure 5, blend nicely with the rest of the WF and provide powerful constructs to bring integrated security in workflow solutions.

image: Workflow Security Pack Activities

Figure 5 Workflow Security Pack Activities

In-Workflow Authorization

In Workflow Services, you can use the standard WCF ServiceAuthorizationManager extensibility to enforce authorization, and that feature works exactly the same as in code-based services. However, in some scenarios (for example, where authorization data is part of workflow), you’d like to delay the authorization decision until actual workflow execution. PrincipalPermissionScope is a server-side activity that brings the CLR PrincipalPermission authorization feature to the workflows. Any child activity placed inside the scope will only execute if the permission demand was successful. This activity looks for the identity information in the incoming WCF security context accessed from the OperationContext. PrincipalPermissionScope is implemented using the same IReceiveMessageCallback mechanism mentioned earlier in this article.

The actual PrincipalPermission demand is enforced against an IPrincipal object based on the value of ServiceAuthorizationBehavior.PrincipalPermissionMode. This extensibility feature enables PrincipalPermissionScope to work with an ASP.NET Role Provider as well as a custom IPrincipal implementation produced by a custom IAuthorizationPolicy. Check out msdn.microsoft.com/library/aa702542 for details on how to configure ASP.NET Role Provider with a WCF service.

Messaging Activities and Authenticated Messaging

Send activity provides the primary way of consuming Web services from within workflows. In most real-world scenarios, these back-end services are secured and require authentication before doing any work. In standard code-based services, credential information is specified using the ClientCredential property exposed on a ChannelFactory and a ClientBase<T> derived proxy class. Using this property, a client can specify the credential it wishes to use before calling the service. Unfortunately, Send activity, which wraps the ChannelFactory, doesn’t expose ClientCredential in WF 4, so some of the scenarios that require explicit credential specification aren’t possible with out-of-box Send activity. Note that Send activity does pick the endpoint behavior configuration from the config file, so you can create a custom endpoint behavior to specify these credentials.

A typical example that requires explicit credentials is calling a service configured to require a UserName/Password. As the ClientCredential property isn’t exposed on the Send activity, there’s no way to specify a UserName/Password. Let’s see how GetUserNameSecurityToken activity from WFSP provides a solution to this and other related scenarios.

In Figure 6, Ping activity is generated by the “Add Service Reference” wizard and is configured to call a service that’s secured to require UserName authentication, as shown in following binding configuration:

<wsHttpBinding>
  <binding name="singleShotUserName">
    <security mode="Message">
      <message clientCredentialType="UserName" establishSecurityContext
        ="false" />
    </security>
  </binding>
</wsHttpBinding>

image: Authenticated Messaging Using a UserName Token

Figure 6 Authenticated Messaging Using a UserName Token

In the preceding workflow, GetUserNameSecurityToken creates a UserNameSecurityToken based on the supplied UserName/Password and enlists it with the ambient SecurityTokenHandle provided by the TokenFlowScope activity. “Workflow Security Pack” applies security at the SecurityToken level as opposed to the ChannelFactory /ClientBase<T> approach of applying security at the credential level. In standard WCF, credentials are used to create security tokens, but WFSP uses security tokens directly and taps into the WCF security layer at the token level rather than the credentials level.

TokenFlowScope is the key activity that enables authenticated messaging and other interesting scenarios. This activity, along with the WorkflowClientCredentials endpoint behavior, flows the enlisted tokens from the workflow layer to the WCF security layer, where they’re attached with the outgoing message as per the binding requirements of the endpoint. TokenFlowScope requires a custom ClientCredential behavior (WorkflowClientCredentials) to be configured, as shown in the following configuration snippet:

<behavior>
   <!--This custom clientCredentials enables the credential flow from 
     workflow data model into WCF security layer. -->
   <clientCredentials
     type="Microsoft.Security.Activities.WorkflowClientCredentials, 
     Microsoft.Security.Activities, Version=1.0.0.0, Culture=neutral, 
     PublicKeyToken=31bf3856ad364e35">
   </clientCredentials>
</behavior>

WFSP follows this exact model when calling a service that requires a token from a Security Token Service (STS), as shown in Figure 7.

image: Fine-Grained Control of SAML Token Acquisition and Usage

Figure 7 Fine-Grained Control of SAML Token Acquisition and Usage

In Figure 7, GetSamlSecurityToken goes to an issuer and acquires a SAML token that’s then enlisted with the ambient handle provided by the TokenFlowScope activity. This enlistment makes this token available to any Send activity living in the same scope and requiring a SAML token. The model is extensible, and GetSamlSecurityToken can itself use an already enlisted token while acquiring a SAML token, for example, if the STS requires a UserName token to return a SAML token and if there’s already a valid UserName token enlisted in the scope. GetSamlSecurityToken, when configured with WorkflowClientCredentials behavior, would use this token when requesting a SAML token.

Out-of-box WFSP only supports UserName and SAML token types; however, other token types can be enabled by inheriting from the GetSecurityToken class as shown in the code snippet in Figure 8, which implements an activity to create an X509-based token.

Figure 8 WFSP Extensibility: Implementing Additional Token Types

[Designer(typeof(GetX509SecurityTokenDesigner))]
public class GetX509SecurityToken : GetSecurityToken
{
  public GetX509SecurityToken()
  {
    FindType = X509FindType.FindBySubjectName;
    StoreLocation = StoreLocation.CurrentUser;
    StoreName = StoreName.My;
  }
  public InArgument<X509Certificate2> Certificate { get; set; }
  public X509FindType FindType { get; set; }
  public StoreLocation StoreLocation { get; set; }
  public InArgument<string> FindValue { get; set; }
  public StoreName StoreName { get; set; }
  protected override void Execute(NativeActivityContext context)
  {
    X509Certificate2 targetCert = null;
    if (this.Certificate != null)
      targetCert = this.Certificate.Get(context);
    if (targetCert == null)
    {
      var store = new X509Store(StoreName, StoreLocation);
      try
      {
        store.Open(OpenFlags.ReadOnly);
        var col = store.Certificates.Find(FindType, FindValue.Get(context), false);
        if (col.Count > 0)
          targetCert = col[0];//Use first certificate mathing the search criteria
      }
      finally
      {
        if (store != null)
          store.Close();
      }
    }
    if (targetCert == null)
      throw new InvalidOperationException(
        "No certificate found using the specified find criteria.");
        // Enlist the token as a flow token
      base.EnlistSecurityToken(context, new X509SecurityToken(targetCert));
  }
}

GetX509SecurityToken creates an X509Security token based on a certificate and enlists it with the SecurityTokenHandle as a flow token that can then be used to call a service requiring a certificate for authentication. Figure 9 shows GetX509SecurityToken in use with a custom activity designer.

image: TokenFlowScope with a Custom GetToken Activity

Figure 9 TokenFlowScope with a Custom GetToken Activity

Claims-Based Delegation

Claims-based delegation is another useful feature enabled by WFSP. Claims-based delegation is often more desirable for workflow services, as these services primarily implement an orchestration/business process calling multiple back-end services. Additionally, access to a caller’s identity in those back-end services is often required to enable fine-grained authorization decisions. WFSP leverages the ActAs functionality of WS-Trust 1.4 to enable any token type to be used as an ActAs token. By default, all the GetToken activities create a token and enlist it as a flow token—however, all of these activities also have a flag known as IsActAsToken, as shown in Figure 10.

image: Creating an ActAs Token

Figure 10 Creating an ActAs Token

When this flag is checked, the token-creation logic stays the same, but the created token T1 is enlisted as an ActAs token rather than a flow token. There can be only one ActAs token per scope, and it’s consumed by GetSamlSecurityToken activity when requesting a SAML token. When GetSamlSecurityToken executes, the active ActAs token is picked up and is sent as part of a token-issuance request generated by the GetSamlSecurityToken activity. The returned token T2 would contain claims from both the authentication token as well as the ActAs token. Finally, any Send activity executing inside this scope can use this T2 token when calling a back-end service that would see both the identities in its security context.

GetBootstrapToken activity is used in middle-tier scenarios to enable end-to-end claims-based delegation. As opposed to GetToken activities, this activity simply reads the incoming token and enlists it as an ActAs token rather than creating a new token and then enlisting it. GetBootstrapToken activity enables a workflow service to use the identity of the incoming caller, in addition to its own identity, when calling back-end services, as shown in Figure 11.

image: End-to-End Claims-Based Delegation Flow

Figure 11 End-to-End Claims-Based Delegation Flow

In Step 3 of Figure 11, workflow service uses WFSP activities to read the incoming bootstrap token, acquires a new token acting as the bootstrap token identity and then flows both identities to a back-end server. Figure 12 shows the workflow that’s used by this workflow service.

image: Claims-Based Delegation Workflow

Figure 12 Claims-Based Delegation Workflow

In the workflow shown in Figure 12, GetBootstrap activity is placed inside an OperationContextScope to guarantee thread-agnostic access to an OperationContext when this activity executes. GetSamlSecurityToken uses the ActAs token produced in the previous step by the GetBootstrapToken activity and then, finally, Echo activity calls the back-end service with a final SAML token produced by the GetSamlSecurityToken activity.

Windows Impersonation/Delegation

ImpersonatingReceiveScope is another server-side activity that brings Windows impersonation and delegation to the workflow world. When this activity executes, it looks for a WindowsIdentity inside the incoming security context. If the incoming message produces a WindowsIdentity, all the child activities that are part of the body will execute inside this impersonated scope. ImpersonatingReceiveScope uses the Workflow TLS mechanism, mentioned earlier in this article, to impersonate the identity on the WF thread just before executing a work item. Impersonation is reverted when the WF work item completes execution.

Failing to find a valid WindowsIdentity in the incoming security context, ImpersonatingReceiveScope looks for a UPN claim—either in a WIF identity (Thread.CurrentPrincipal) or in the traditional WCF ClaimsSet—and uses it to create a WindowsToken using the S4U features of Kerberos. To transform a UPN claim to a Windows token, ImpersonatingReceiveScope relies on “Claims to Windows Token Service,” which is part of the WIF runtime. This service must be installed and running for the claim-to-token transformation to be successful.

Figure 13 shows a typical use of ImpersonatingReceiveScope activity.

image: ImpersonatingRecieveScope in Action

Figure 13 ImpersonatingRecieveScope in Action

End-to-End Security

From the outside, workflow services are standard WCF services, and as such, most of the WCF security options are applicable to workflow services as well. WF 4 introduced a couple of key extensibility points that can be used to further extend the workflow services security boundary to the workflow layer. WFSP provides a collection of activities that use these extensibility points to bring end-to-end security to WF 4.


Zulfiqar Ahmed is a senior consultant on the Premier Support for Developers team and is the original author of the Workflow Security Pack project. He maintains a blog at zamd.net, where he covers topics ranging from Windows Communication Foundation, Windows Workflow Foundation and claims-based security to general Microsoft .NET Framework stuff.

Thanks to the following technical expert for reviewing this article: Dave Cliffe