Task 2: Enable Claim-based Authorization on the Workflow Service

Download sample

In this task, you ensure that only one set of user credentials can invoke operations on the workflow service you created in Exercise 1: Create a Basic Workflow Service. These credentials are passed to the service in the form of a ClaimSet. For more information about ClaimSets, see Managing Claims and Authorization with the Identity Model.

Note

When the Visual Studio workflow designer is used to create or manage workflow services, it sometimes produces spurious validation errors. If you are able to successfully build your project, ignore the validation errors.

Enable User Validation through ClaimSets

  1. If you do not currently have the WorkflowServiceTutorial solution open, then open Visual Studio 2008, click File, highlight Open, and navigate to the WorkflowServiceTutorial solution.

  2. In the Solution Explorer pane, under the WorkflowServiceTutorial project node, right-click the References subfolder and select Add Reference. Alternatively, if you created a Visual Basic solution, right-click the WorkflowServiceTutorial project node and select Add Reference.

  3. In the Add Reference dialog box, under the .NET tab, select System.IdentityModel and click OK.

  4. The ClaimSet and List objects are used in this task, so add the following using statements at the top of Workflow1.cs:

    using System.IdentityModel.Claims;
    using System.Collections.Generic;
    

    If you created a Visual Basic solution, right-click the WorkflowServiceTutorial project node and select Properties. Select the References tab, and under Imported namespaces, click the check boxes for System.IdentityModel.Claims. The System.Collections.Generic namespace is already supported.

  5. If the workflow designer for the workflow service is not visible, open the designer by right-clicking on Workflow1.cs (or Workflow1.vb if you created a Visual Basic solution) and select View Designer.

  6. In the Workflow1InitialState StateActivity activity, double-click the WaitToStartService EventDrivenActivity activity to expand that composite activity.

  7. Highlight the ReceiveActivity activity associated with the StartupService operation.

  8. In the Properties pane, under OperationValidation, type in ValidateUser and press Enter to auto-generate an event handler for the OperationValidation event.

  9. Navigate to the ValidateUser event handler.

    In the body of ValidateUser, verify that the user has been previously recognized by the service and if not, do not let them invoke any operations. For example, this is useful if a sales employee starts a purchase order and does not go back to complete it until several days later. You should verify that it is the same user before letting the user invoke any more operations on the service. Conversation and context IDs cannot be used because they can be impersonated by malicious users.

        Private Sub ValidateUser(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.OperationValidationEventArgs)
            For Each claims As ClaimSet In e.ClaimSets
                ' Find the claim that contains the name of the operation caller.
                Dim opCaller As List(Of Claim) = claims.FindClaims(ClaimTypes.Name, Rights.PossessProperty).ToList()
    
                ' Retrieve the name of the caller from the claim.
                Dim opCallerName As String = opCaller(0).Resource.ToString()
    
                ' If this is the caller's first time through the ValidationUser method, set 
                ' the operation caller's name to a global variable named "owner." Every subsequent
                ' operation that uses this method will verify that the caller of
                ' the operation is the same as the caller of the initial operation before
                ' either validating or invalidating the caller.
                If [String].IsNullOrEmpty(owner) Then
                    owner = opCallerName
                ElseIf Not owner.Equals(opCallerName) Then
                    e.IsValid = False
                End If
            Next
        End Sub
    
    private void ValidateUser(object sender, OperationValidationEventArgs e)
    {
        foreach (ClaimSet claims in e.ClaimSets)
        {     
            // Find the claim that contains the name of the operation caller.
            List<Claim> opCaller = claims.FindClaims(ClaimTypes.Name, Rights.PossessProperty).ToList<Claim>();
    
            // Retrieve the name of the caller from the claim.
            string opCallerName = opCaller[0].Resource.ToString();
    
            // If this is the caller's first time through the ValidationUser method, set 
            // the operation caller's name to a global variable named "owner." Every subsequent
            // operation that uses this method will verify that the caller of
            // the operation is the same as the caller of the initial operation before
            // either validating or invalidating the caller.
            if(String.IsNullOrEmpty(owner))
            {
                owner = opCallerName;
            }
            else if (!owner.Equals(opCallerName))
            {
                e.IsValid = false;
            }
        }
    }
    
  10. Declare a variable named "owner" to use as validation when receiving subsequent operation invocations as shown in the following code:

    Public class ServerWorkflow
        Inherits StateMachineWorkflowActivity
    
        ' These variables are bound to the input and output parameters of the ReceiveActivity.
        Public returnValue As Int32 = Nothing
        Public inputValue As Int32 = Nothing
    
        'This variable contains the user name for the NT account used in operation validation.
        Public owner As String = Nothing
    ...
    End Class
    
    public sealed partial class ServerWorkflow : StateMachineWorkflowActivity
    {
        public ServerWorkflow()
        {
            InitializeComponent();
        }
    
        // These variables are bound to the input and output parameters of the ReceiveActivity.
        public int returnValue = default(int);
        public int inputValue = default(int);
    
        // This variable contains the user name for the NT account used 
        // in operation validation.
        public string owner = default(string);
        ...
    }
    
  11. For each of the remaining operations, associate the OperationValidation event with the ValidateUser method.

  12. Build your solution and verify that your authorization checks work.

    If a user with a different user name tries to invoke an operation on the service after the owner variable has already been set, then the following fault message will be returned to the client:

    Security check failed.
    

    Note

    A ClaimSet is processed before PrincipalPermissionRole or PrincipalPermissionName, so if you perform two different authorization checks against an NT account group, one using a ClaimSet and one using PrincipalPermissionRole, the ClaimSet authorization check will happen first.

See Also

Other Resources

Task 1: Enable Role-based Authorization on the Workflow Service
Exercise 2: Implement Security Features on the Workflow Service

Copyright © 2007 by Microsoft Corporation. All rights reserved.
Last Published: 2010-03-04