Zulfiqar's weblog

Architecture, security & random .Net

Integrating WIF, WF 4.0 & AppFabric: Claims-Based-Delegation

Posted by zamd on June 3, 2010


Extending upon my last post, where I have talked about basic integration, this post will go into the details of claims-based-delegation in WF 4.0 & AppFabric.

Again I’ll be using activities from the Workflow Security Pack. Let’s start with a diagram which captures the main components of solution and their interactions:

image 

  1. Unauthenticated user browse to web application protected by WIF modules.
  2. WIF redirects user to Passive STS for authentication
  3. User authenticates @ passive STS and is redirected back to the ASP.net app along with the Issued Token. WIF modules processes this token and upon successful validation of the token, user is logged into the application. I have configured SaveBootstrapTokens on this app, so the raw incoming token is preserved as part of IClaimsIdentity.
  4. Users click the “Call Service” link to invoke following client workflow. GetBootstrapToken activity reads the bootstrap token and enlist it with the SecurityTokenHandle (specified on the InitializeActAsToken activity) as an ActAs token.image
  5. InitializeSamlSecurityToken activity issues a request (RST) to acquire a SAML token from the STS using the ActAs token enlisted in step 4. InitializeSamlSecurityToken is able to see the ActAs token (enlisted by InitializeActAsToken activity)  because both of these activities share the same SecurityTokenHandle. At this stage, Web App is authenticated by STS using windows authentication while ActAs token is a Saml token (acquired using forms authentication in step 3). The final Saml token will contain claims for both immediate (web app) and original caller (authenticated user).
  6. TokenFlowScope activity along with the workflowCredentials behaviour (configured on the endpoint used by the Echo activity) enhances the WCF security pipeline to attach the Saml token (acquired in step 5) with the outgoing message. As part of it’s execution, TokenFlowScope will detect that Echo activity requires a Saml token, it will then check it’s enlisted tokens to see if it can satisfy the token requirements of the Echo activity. In this example, there is already a Saml token enlisted with the handle so TokenFlowScope simply attaches that token with the outgoing message.

I have attached complete solution with this post. I tried to keep the solution self-contained by using file-based certificates and other shortcuts so hopefully you should be able to get it working by just hitting Ctrl-F5 🙂

Feel free to download and experiment and let me know your thoughts…

Advertisements

10 Responses to “Integrating WIF, WF 4.0 & AppFabric: Claims-Based-Delegation”

  1. scott_m said

    Great post and sample.

    I did have an issue though when I ran on my box (VS2010 / Win 2008 R2). I get the following error after clicking the submit button the login page:

    System.Exception was unhandled by user code
    Message=An unexpected error occurred when processing the request. See inner exception for details.
    Source=App_Web_fjtq1aid
    StackTrace:
    at _Default.Page_PreRender(Object sender, EventArgs e) in c:\Dev\CSharp\WIF\WF-Claims-based-delegation\MvcApplication2_STS\Default.aspx.cs:line 69
    at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
    at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
    at System.Web.UI.Control.OnPreRender(EventArgs e)
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
    InnerException: System.InvalidOperationException
    Message=The action ” (Request.QueryString[‘wa’]) is unexpected. Expected actions are: ‘wsignin1.0’ or ‘wsignout1.0’.
    Source=App_Web_fjtq1aid
    StackTrace:
    at _Default.Page_PreRender(Object sender, EventArgs e) in c:\Dev\CSharp\WIF\WF-Claims-based-delegation\MvcApplication2_STS\Default.aspx.cs:line 58
    InnerException:

    Looks like it was from:

    MVCApplication2/Default.aspx
    .
    .
    .
    catch ( Exception exception )
    {
    throw new Exception( “An unexpected error occurred when processing the request. See inner exception for details.”, exception );
    }

  2. scott_m said

    Got a little further. I set the mvcapplication2_sts to not auto start and wait for an external app to attach.

    Now when this code executes for this first time:

    “var output = WorkflowInvoker.Invoke(new CallerService());”

    I get:

    “System.TimeoutException was unhandled by user code
    Message=Client is unable to finish the security negotiation within the configured timeout (00:00:59.9970000). The current negotiation leg is 1 (00:00:59.9960000).
    Source=System.Activities
    StackTrace:
    at System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
    at System.Activities.WorkflowInvoker.Invoke(Activity workflow, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)
    at System.Activities.WorkflowInvoker.Invoke(Activity workflow)
    at MvcApplication2.Controllers.ServiceController.Call() in C:\Dev\CSharp\WIF\WF-Claims-based-delegation\MvcApplication2\Controllers\ServiceController.cs:line 23
    at lambda_method(Closure , ControllerBase , Object[] )
    at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
    at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
    at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
    at System.Web.Mvc.ControllerActionInvoker.c__DisplayClassd.b__a()
    at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
    InnerException: System.TimeoutException
    Message=The request channel timed out while waiting for a reply after 00:00:59.9850000. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout.
    Source=System.ServiceModel
    StackTrace:
    at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
    at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
    InnerException: System.TimeoutException
    Message=The HTTP request to ‘http://localhost:9000/STS/Trust13’ has exceeded the allotted timeout of 00:00:59.9900000. The time allotted to this operation may have been a portion of a longer timeout.
    Source=System.ServiceModel
    StackTrace:
    at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
    at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
    at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
    InnerException: System.Net.WebException
    Message=The operation has timed out
    Source=System
    StackTrace:
    at System.Net.HttpWebRequest.GetResponse()
    at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
    InnerException:

    If I run the same code a second time, it works great.

  3. scott_m said

    Thats pretty cool how you use your CallService.xaml as a facade for calling the back-end wf service. In the example, the Echo WF service appears to be configured (URL / bindings) via app.config. In my case the actual WF service url will not be known until runtime. Is there a way to dynamically set the WF Service URL for the client?

    thanks

    • zamd said

      Unfortunately “Add Service Reference” generated activities (like the Echo activity in sample) doesn’t allow you to change the endpoint address dynamically. You would have to use plain Send/ReceiveReply activities for this.

  4. scott_m said

    I was looking at the TokenFlowHandler used by the TokenFlowScope. From a high level, looks like it uses the TokensExtension to provide the hook to inject the W.I.F. token into the message prior to actual transmission. I noticed the TokensExtension Atach/Detach interface methods were empty but there was a method called “FindToken” that looked like it did something. Looks like the GenericSecurityTokenProvider is responsible for calling “FindToken”. How does GenericSecurityTokenProvider get wired into the WCF pipeline?

    thanks

  5. Alejandro said

    Hello Zamd,

    We are implementing a scenario similar to what you describe. We have several Azure hosted WCF services which trust a local active STS, and a web application which trusts a local passive STS.
    The latter STS has a trust relationship with LiveID. This part of stage is working properly. The user tries to access the web application, it forwards the local STS, which returns to redirect to LiveID. Once the user logs in, he is redirected to the local STS again, with the Claim of Live, which transforms it and gets the claims our web site requires, and redirects the user back to the web site. When the application needs to invoke a service, it use CreateChannelActingAs with Bootstrap token, in order to copy the tokens in the local Active STS and then perform the service invocation.

    Furthermore, we need to invoke the services from a desktop application. To implement this login to Live as follows:

    var issuer = “https://dev.login.live-int.com/wstlogin.srf”;
    var binding = new WSHttpBinding();
    binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
    binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
    binding.Security.Message.EstablishSecurityContext = false;
    binding.Security.Message.NegotiateServiceCredential = false;
    WSTrustChannelFactory fact = new WSTrustChannelFactory(binding, new EndpointAddress(issuer));
    fact.TrustVersion = TrustVersion.WSTrustFeb2005;//.WSTrust13;
    var unp = fact.Credentials.UserName;
    unp.UserName = usuario;
    unp.Password = password;

    var client = fact.CreateChannel();

    var rst = new RequestSecurityToken(WSTrustFeb2005Constants.RequestTypes.Issue);
    rst.AppliesTo = new EndpointAddress(“http://live-int.com”);

    RequestSecurityTokenResponse rstr;
    SecurityToken bootstrapToken = client.Issue(rst, out rstr);

    After the user enters Live and get the token we want to invoke the services.

    ChannelFactory factory = new ChannelFactory(“CustomBinding_ISecSpot”);
    factory.Credentials.ServiceCertificate.SetDefaultCertificate(“CN=localhost”, StoreLocation.LocalMachine, StoreName.My);
    factory.ConfigureChannelFactory();

    ISecSpotChannel channel = factory.CreateChannelActingAs(bootstrapToken);
    channel.MyMethod()

    This is where we face the problem as trying to go through the local Active STS, we get an error.
    Is this the correct way? Is there any way to resolve what we are trying?

    Thks, Alejandro

  6. zamd said

    Hi,
    I don’t believe https://dev.login.live-int.com/wstlogin.srf endpoint is fully functional and supports your active/WS-Trust scenario.
    Last I looked, there was no public way to register a Relying party (in your case your local STS) with LiveID STS. Even though you get back a token but it is encrypted & meant for some internal LiveID properties and NOT your STS/RP.

    HTH,
    Zulfiqar

  7. […] Integrating WIF, WF 4.0 & AppFabric: Claims-Based-Delegation June 2010 9 comments 5 […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: