BizTalk, Claims and Windows Identity Foundation

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 <behaviorExtensions> 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.

8 thoughts on “BizTalk, Claims and Windows Identity Foundation”

  1. Great post, Magnus! I think it’s worth pointing out that the STS that generates the claims can be any STS, not just a custom one built w/ WIF. Obvious to you, but not necessary every reader.

  2. Thanks for the positive feedback! You are right, i added a note in the article that any STS with WS-Trust support will work. I have been doing some testing with the WSO2 Identity Server and it worked great.

    Good luck with your move to Sweden, im sure you will like it here 🙂

  3. Hi Magnus. Very useful approach – a lot of customers seems to struggle with this issues. Like the way You stay on the track by using both WCF and BizTalk as it is meant to be used without tweeks and mystical tricks 😉

  4. Thanks for this post, I’m working in this area at the moment and found this really useful.

  5. Great post!!!. Your STS can be ADFS server as well. Hey just curious if you have tried to consume a claims aware wcf service. I am about to do a project where the services are claims aware. Any pointer/ tricks may help

Leave a Reply

Your email address will not be published. Required fields are marked *