Vittorio Bertocci

Scatter thoughts

A Silver CardSpace: securing Orcas Workflow Services with Windows CardSpace

A Silver CardSpace: securing Orcas Workflow Services with Windows CardSpace

  • Comments 5

In short: this is a step by step tutorial for creating from scratch a Workflow Service with the Beta 1 release of Visual Studio codename "Orcas". The tutorial shows how to secure the service with Windows CardSpace, how to create a client application on the fly and how to access claims from the code of a Workflow activity.

Just days before the Earth-moving news at Mix, with the Beta 1 release of Visual Studio codename "Orcas" we made available another silvery technology: the Workflow Services, Silver for friends, are an exciting new technology which allows developers to blend WCF and WF for creating service-aware workflows. As in good tradition, one of the first things I thought about was how to secure those new breed of services via CardSpace: turns out that is incredibly easy, and I could explain it in a 1/2 post if I'd start from an existing workflow service project. However Silver technology is still cutting edge: so I thought it could have been useful to make a full walkthrough. EDIT: after some hours spent writing this post, I've seen that the WF overlord already covered the workflow creation part and in better details: I recommend you checking Matt's post out, especially if some of the passages below are obscure to you.  

The plan

We'll partition the work in few steps:

1. Create the workflow project

2. Add and configure the Receive activity

3. Host the workflow in  a WorkflowServiceHost

4. Configure the workflow endpoint for using CardSpace

5. Create a client project on the fly

6. Test run

7. Add claim handling to the workflow and re-run

Hmm, this is going to be long. I may record a screencast and it would be waaaay faster for me (I'll probably do it anyway sooner or later), but it would not be searchable and you could not fastforward through it: so I've just to live with the fact that I'll spend the next few hours on this :-) bring it on!

1. Create the Workflow project

There are few prerequisites for this tutorial:

  • You need the Beta 1 of Visual Studio codename "Orcas" (hey friends from marketing, if you're watching: see how nicely I use the proper naming?:P)
  • You should download the new WCF samples and go through the one time installation procedure
  • The CardSpace portion of the tutorial relies on the usual adatum-issued Fabrikam certificate, that can be found (and installed with) this sample. Any other certificate will do, provided you change the references in the config accordingly

Without further ado, let's dive. Open Visual Studio and create a new "Sequential Workflow Console Application" project.

Create an arbitrary workflow: for keeping things simple, you can drop just a classical code activity with some Console.Writeline in the body.

So far, business as usual. Hit F5 just to make sure that you didn't forget a semicolon here and there, and we're done.

2. Add and configure the Receive activity

Here things start to get interesting. Let's assume that you want to expose your workflow as a WCF service. Traditionally ("traditionally"? we shipped 3.0 just months ago :-D I *love* working in Evangelism) you would have had to write a fair amount of plumming. With Silver things are dramatically easier: for a start, you can represent the service operation in your workflow as easily as dragging in an activity that represents the act of receiving a message.

On the left side of the image you can see that in Orcas the Workflow toolbox has an extra set of activities, enclosed in the red rectangle. Drag a receive activity in the first activity slot of the workflow, as shown in the figure; then you can move your workflow directly inside the new receive activity.

We now have a visual moniker for our service, directly in the workflow! In the next steps we supply details about the service itself. The designer guides us through the task: the familiar red excalamation point on the top right corner of the activity suggests the next step by point ing out that "Activity 'receiveActivity1' does not have a service operation specified.". Clicking on the message brings us to the relative property page as shown below:

Let's go ahead and click on the ServiceOperationInfo ellipsis. We get prompted by the following dialog:

Of course! If we want our workflow to be invoked as a service, we need to map it in to a method; and such a method must belong to a contract. The "Import..." button on the top right corner of the dialog suggests that we can import existing contracts: that comes very handy for real world application. The other button, "Add Contract", enables us to take a workflow-first approach and create our service contract on the fly. In the tutorial we are going to create a contract from scratch, and for the sake of clarity is going to be a pretty lame one. Let's click on the "Add Contract" button and let's fill the dialog in with something like the screen shown below:

As I promised, the contract is pretty lame :) there's one single operation, "UnleashMyAppLogic", which takes no parameters nor it returns anything (I'm sure that subsequent posts will go in details about  how to deal with I/O).

When we return from the dialog we discover that the receiveActivity glyph is now labeled with the name of the operation it models. Great, that helps understanding what the workflow does.

We still have a couple of task for completing this step:

  • in the property page you can see I highlighted in red the property CanCreateInstance: that property tells to the runtime if the operation we are mapping has the right of creating a workflow instance, or if it should work only if associated to an existing workflow instance. In our case, the operation is what actually starts the workflow: we certainly want it to be able to create an instance, so we set the property to true
  • In the References of the solution I highlighted in green an assembly, SMDiagnostics; it gets inserted by mistake (beta1 bug). You need to delete that reference, otherwise it won't compile.

3. Host the workflow in  a WorkflowServiceHost

Now that we modified our workflow for acting as a service, we need to provide an adequate hosting environment.

Silver provides a new hosting class, WorkflowServiceHost, that is a crossing between the WorkflowRuntime and the ServiceHost. You can feed WorkflowServiceHost with a workflow class representing a workflow service, like the one we created at the step above, and it will take care of creating the endpoint and handling requests for you. Sweet :-)

You can open the code of Program.cs and substitute the Main function with the code below:

 static void Main(string[] args)

 {

    WorkflowServiceHost workflowHost = new WorkflowServiceHost(typeof(SilverCardSpaceWorkflowService.Workflow1));

   

    workflowHost.WorkflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e) { Console.WriteLine("WorkflowTerminated: " + e.Exception.Message); };

    workflowHost.WorkflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { Console.WriteLine("WorkflowCompleted: " + e.WorkflowInstance.InstanceId.ToString()); };

   

    workflowHost.Open();

    Console.WriteLine("I am listening.");

    Console.WriteLine("Press <enter> to exit.");

    Console.ReadLine();

    workflowHost.Close();

 }

 The line highlighted in yellow initializes the WorkflowServiceHost with our workflow (artfully left with its default class name, Workflow1). The lines highlighted in light blue show how WorkflowServiceHost behaves practically like ServiceHost.  WorkflowServiceHost lives in  System.ServiceModel, too: you will need to add the suitable using clause.

The project compiles, but we are not ready for prime time yet: we need to perform the next step before being able to accept incoming requests.

4. Configure the workflow endpoint for using CardSpace

Don't get sidetracked by the title of this step: you need to configure the workflow service endpoint, cardspace or not cardspace. Here we just happen to use a binding that will involve cardspace in the picture.

What we need to do is simple: we have to add a configuration file to the project, and we have to use it for defining the details of our service and the endpoints we want to expose.

Right click on the project and add a new item, namely an "Application Configuration File". We will then paste in it the following services section:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <!-- <service name="SilverCardSpaceWorkflowService.Workflow1"

               behaviorConfiguration="ServiceBehavior" >

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost:8888/ServiceHost/Workflow1.svc" />

          </baseAddresses>

        </host>       

        <endpoint address=""

                  binding="wsHttpContextBinding"

                  contract="IMyAppContract" />

      </service>-->

      <service

              name="SilverCardSpaceWorkflowService.Workflow1"

              behaviorConfiguration="CardSpaceServiceBehavior">

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost:8888/ServiceHost/Workflow1.svc" />

          </baseAddresses>

        </host>

        <endpoint

              address=""

              contract="IMyAppContract"

              binding="wsHttpBinding"

              bindingConfiguration="CardSpaceBinding">

          <identity>

            <certificateReference

              findValue="www.fabrikam.com"

              storeLocation="LocalMachine"

              storeName="My"

              x509FindType="FindBySubjectName" />

          </identity>

        </endpoint>

        <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />

      </service>

    </services>

The yellow code contributes to the definition of the service and the endpoint. The name of the service matches the class name of the workflowe implementing the service, SilverCardSpaceWorkflowService.Workflow1; the base address represents the address that clients will use for invoking the service; the contract is of course the contract we defined in our workflow-first steps described above. All the light blue parts define security information necessary for identifying the service via e certificate and instruct clients to comply to wsHttpBinding. Together with the binding and behavior configuration, shown below, those settings will elicit clients to use cardspace every time they will invoke this service.

    <bindings>

      <wsHttpBinding>

        <binding name="CardSpaceBinding">

          <security mode="Message">

            <message clientCredentialType="IssuedToken"/>

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

 

    <behaviors>

      <serviceBehaviors>

        <behavior name='CardSpaceServiceBehavior' returnUnknownExceptionsAsFaults='true' >

          <serviceMetadata httpGetEnabled="true" />

          <serviceCredentials>

            <issuedTokenAuthentication allowUntrustedRsaIssuers='true' />

            <serviceCertificate

              findValue='www.fabrikam.com'

              storeLocation='LocalMachine'

              storeName='My'

              x509FindType='FindBySubjectName' />

          </serviceCredentials>

        </behavior>

      </serviceBehaviors>

    </behaviors>

   

  </system.serviceModel>

</configuration>

This is exactly business as usual, there is nothing in this config that suggests we may not be using a normal WCF service. 

Note that Silver actualy introduces its own binding, wsHttpContextBinding, which is the key for enabling a number of great functionalities such as durable services, automatic dispatch of the call to the right workflow instance and so on (you can see an example of that in the code above, in the commented portion). I strongly encourage you to study the samples and keep an eye on Matt's blog for more information on the above. Here we are using the wsHttpBinding because we want to play with cardspace.

5. Create a client project on the fly

No we are finally ready to give the workflow service a run. Make a good Control+F5 (start withoud debugging) and you'll see the console version of Dr. Fraiser.

If you open a browser and you visit the service address (you can copy it from the config file: http://localhost:8888/ServiceHost/Workflow1.svc) you'll see the classical metadata page.

A client for our workflow service does not need to do anything fancy, just a call to the method we exposed: with the "add service reference" function we are going to set it up in no time, so let's keep this instance running: we'll need it for getting the necessary metadata.

Create a new console project in the same solution:

Right click on the References folder of the newly created project and choose "add service reference". Do you still have the service address on the clipboard? Good, we are going to need it: paste it in the add service reference dialog, visual studio will query the running instance of the service host and will reference it on the fly.

That takes care of everything, including creating the suitable app.config.

At this point you just need to call the service from the Main function in Program.cs:

static void Main (string[] args)

{

 ServiceReference.MyAppContractClient macc = new   Workflow1ConsoleClient.ServiceReference.MyAppContractClient();

              macc.ClientCredentials.ServiceCertificate.Authentication.RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck;

 

Console.WriteLine("Hit Enter for unleashing invoking the workflow service");

Console.ReadLine();

macc.UnleashMyAppLogic();

Console.WriteLine("The workflow service was invoked. Press Enter to exit");

Console.ReadLine();

 

}

 

The code highlighted in yellow makes all the work: it istantiates a proxy and it invokes it.

The code highlighted in blue is a little workaround for turning off the revocation check of the test certificate: normally you would not want to turn this check off, this is only for testing purposes.

6. Test run

When you describe the process it seems long, but it is actually pretty quick to set up.

We can now finally make our test run: set the solution to multiple starttup projects, set both project to "start" and we are good to go! Hit F5 and see what happens.

In the background you have the workflow service host process, in the foreground the client. Hit enter in the client console.

As imposed by the binding configuration we assigend to the workflow service, here there's our familiar cardspace UI! Let's send the card and see if the call goes OK:

It does. We can see in the service console that everything went as expected.

We can officially rejoice: we invoked a workflow service using cardspace!!!

7. Add claim handling to the workflow and re-run

In fact, as of now the service is not doing much with the token it gets from cardspace. Accessing the token from the workflow activity code is very easy, you use the same mechanisms you would use in a classical WCF service. Modify the code activity source, located in Workflow1.cs, as follows:

private void ApplicationLogicBody(object sender, EventArgs e)

{

    Console.WriteLine("Oh my. Ive been invoked.");

 

     AuthorizationContext ac = ServiceSecurityContext.Current.AuthorizationContext;

 

     foreach (ClaimSet ct in ac.ClaimSets)

        foreach (Claim c in ct)

         {

           if (c.ClaimType == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier")

              {

                  Console.WriteLine("The PPID is " + c.Resource.ToString());

                  break;

              }

          }

 }

The code highlighted in yellow takes care of retrieving the AuthorizationContext, from which we can access claim values and other details of how the call has been secured. We can base out authentication and authorization decision directly from those data. Note that the compiler is not going to like the code above until you added all the necessary "using": luckily the visual studio smart tags make the operation quick and painless.

A re-run of the sample will show that we can actually access the claims:

As highlighted with the red rectangle, we show the incoming PPID. Neat!

Conclusion

We have exposed a Workflow as a service without writing any plumming code; and we added cardspace support just by adding the right configuration, the same we already used for pure WCF+cardspace samples. For the lazy among you, I am going to attach the ZIP of the sample we created through this tutorial. If you have questions, feel free to send them my way.

Happy Silver+Cardspace playing! :-)

  • I haven't had a shot at implementing this yet, but it seems that it would be a nice demonstration of CardSpace, WF and the Biztalk Internet Services Bus - BizTalk Services.

    Your thoughts?

  • Vittorio in the team has just completed a tutorial on his blog which walks through 5 very simple steps

  • In the last loooong post , the one about using CardSpace together with the new Receive activity featured

  • Hey Daniel,

    how is it going :)

    The thought is interesting. I haven't played much with the BTS ISB yet, so I wouldn't know how to give specific advice for the time being. For what I've seen it should be easily doable, provided that you build the wsHttpBinding via imperative code and you use the relay binding in configuration. But, again, I've yet to find the time to play with it. Let me know how it goes ;-)

  • In short: this is the description of a sample that sends a CardSpace-obtained token to an AJAX service

Page 1 of 1 (5 items)
Leave a Comment
  • Please add 7 and 3 and type the answer here:
  • Post