BizTalk, Claims and Windows Identity Foundation

| January 09, 2011 | 4

Being a fan of Microsoft’s Windows Identity Foundation and its claims and federation based approach to identity i have been interested in how well it fits into BizTalk. Even though BizTalk does not provide any built-in support for the Windows Identity Foundation classes its support for WCF-bindings such as WS2007FederationHttpBinding gives support for WS-Trust and SAML tokens issued by external Security Token Services (STS).

I this scenario a Security Token Service (STS) are used to issue tokens to clients who are using the WS-Trust protocol and the WS2007FederationHttpBinding. The token is passed on to the service hosted in BizTalk. I use an test-STS based on Windows Identity Foundation but any STS supporting the WS-Trust protocol will do.

[

Claims-based authorization

With BizTalk hosting the service, as in any WCF service hosting scenario, authentication and authorization of the caller are best made in the WCF-channel. As the WIF-based extension ClaimsAuthorizationManager is not an option, i can use the standard WCF ServiceAuthorizationManager to determine access as i do in the simplified example below. In this scenario only persons of 18 years or older, based on a date-of-birth claim inside the received token, will be allowed access.

public class GottorpServiceAuthorizationManager : ServiceAuthorizationManager
    {
        protected override bool CheckAccessCore(OperationContext operationContext)
        {
            foreach (ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
            {
                foreach (System.IdentityModel.Claims.Claim c in cs.FindClaims("http://soapfault.com/claims/dateofbirth", Rights.PossessProperty))
                {
                    int age = new DateTime(DateTime.Now.Subtract(DateTime.Parse(c.Resource.ToString())).Ticks).Year-1;

                    if (age >= 18)
                        return true;
                }
            }
            return false;
        }
    }

 

You specify the ServiceAuthorizationManager by adding a ServiceAuthorization behavior and specify the class in the serviceAuthorizationManagerType property as seen below.

[

Claims inside BizTalk

One obvious thing missing with BizTalk is that the WCF-adapters does not flow the claims received into the BizTalk message-context where it could be used for routing, business-logic or similar. As a comparison, in ASP.NET or WCF an IClaimsPrincipal provided by WIF will contain the claims but in BizTalk they are lost when the message hits the BizTalk messagebox.

In scenarios where access to the claims inside BizTalk are needed one solution is to copy them to the message-context with the help of a custom WCF MessageInspector. This will place the claims into the message header that later, by the use of a brilliant built-in WCF-adapter feature, will automatically get transferred to the message-context. The code below is a simplified example on how this can be done with token claims.

public partial class BizTalkContextClaimsMessageInspector : IDispatchMessageInspector
    {
        const string WRITE_PROPERTIES_NAMESPACE =   "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties/WriteToContext";
        const string PROMOTE_PROPERTIES_NAMESPACE = "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties/Promote";
        const string CLAIMTYPE_TO_COPY = "http://soapfault.com/claims";

        public object AfterReceiveRequest(
            ref System.ServiceModel.Channels.Message request,
            System.ServiceModel.IClientChannel channel,
            System.ServiceModel.InstanceContext instanceContext)
        {

            var context = OperationContext.Current;
            var properties = new List<KeyValuePair<XmlQualifiedName, object>>();

            foreach (ClaimSet cs in context.ServiceSecurityContext.AuthorizationContext.ClaimSets)
            {
                foreach (Claim c in cs.Where(q => q.ClaimType == CLAIMTYPE_TO_COPY))
                {
                    var prop = new XmlQualifiedName(
                        c.ClaimType.Substring(c.ClaimType.LastIndexOf("/") + 1),
                        c.ClaimType.Substring(0, c.ClaimType.LastIndexOf("/")));
                    properties.Add(new KeyValuePair<XmlQualifiedName, object>(prop, c.Resource));
                }
            }

            //use WRITE_PROPERTIES_NAMESPACE or PROMOTE_PROPERTIES_NAMESPACE
            request.Properties[PROMOTE_PROPERTIES_NAMESPACE] = properties;

            return null;
       }

 

I can choose if the values simply should be written to the message context or if they also should be promoted by selecting one of two namespaces when writing the request (see code sample above). Note that if they are to be promoted, which is necessary to do routing, a property-schema must be deployed and the value must not exceed 256 characters.

As with any WCF MessageInspector i also need an EndpointBehavior and BehaviorExtensionElement to place it into the WCF-channel.

public class BizTalkContextClaimsEndpointBehavior : IEndpointBehavior
    {

        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
            return;
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            return;
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            var messageInspector = new BizTalkContextClaimsMessageInspector();
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(messageInspector);
        }

        public void Validate(ServiceEndpoint endpoint)
        {
            return;
        }
    }

    class BizTalkContextClaimsBehaviorExtensionElement : BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get
            {
                return typeof(BizTalkContextClaimsEndpointBehavior);
            }
        }

        protected override object CreateBehavior()
        {
            return new BizTalkContextClaimsEndpointBehavior();
        }
    }

To activate it should be registed in the section in machine.config. Note that if you are running on 64-bit host do this in both 32 & 64 -bit machine.config as the MMC-based BizTalk Server Administration Console always runs in 32-bit. After registration and restarting Administration Console i can add the EndpointBehavior on my ReceiveLocation.

Now when a request is received and the message is published in BizTalk messagebox the claims are available in the message context.

They can now be used in orchestrations, pipeline components or for routing purposes.

comments powered by Disqus