Question

I've had quite the challenge in handling the following scenario.

  1. I want to use the Unity DI Framework to create a new channel for my service when needed.
  2. The service is secured with Federated security.
  3. The service is not called from within a service hosted within IIS, but called from within a self-hosted WCF service.

My current issue is occurring in step 3 above - how do I bootstrap the token and yet then satisfy the above 2 requirements also?? I'll outline the solutions to each step though as they're non-trivial and will hopefully assist someone else with this issue.

Solving issue 1: The following code-snippet will create a new instance of the channel anytime one is required.

container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
    new InjectionFactory(
        x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
            .CreateChannel()));

Here's some examples of people doing this better than I: http://unity.codeplex.com/discussions/211736 https://gist.github.com/tracker1/5675161

You can also use alternative lifetime managers as well e.g. the TransientLifetimeManager would work well here.

Solving Issue 2: Now the real difficulty begins - how do I include a security token with the InjectionFactory? Clearly I'm going to want to use CreatChannelWithIssuedToken, but I'm going to need to grab the bootstrap token to do so. This is fairly well documented around the net, e.g. here: http://www.cloudidentity.com/blog/2012/11/30/using-the-bootstrapcontext-property-in-net-4-5-2/ Some important things to note: make sure that you have a serviceBehaviors section in the config that specifies useIdentityConfiguration="true" otherwise your system.identityModel section will be ignored e.g.

  <serviceBehaviors>
    <behavior name="">
      <serviceCredentials useIdentityConfiguration="true" />
    </behavior>
  </serviceBehaviors>

And then your system.identityModel section should also feature:

<system.identityModel>
  <identityConfiguration>
    <securityTokenHandlers>
      <securityTokenHandlerConfiguration saveBootstrapContext="true" />
    </securityTokenHandlers>
  </identityConfiguration>
</system.identityModel>

Then, provided that you've made a request to set this correctly on the session (see my other question: AJAX call against REST endpoint secured with Thinktecture's IdentityServer STS it will be available in the session whenever you access the bootstrap context security token like so:

var bootstrapContext = ((ClaimsIdentity) Thread.CurrentPrincipal.Identity).BootstrapContext 
    as BootstrapContext;
SecurityToken securityToken = bootstrapContext.SecurityToken;

You can then either change your InjectionFactory to look like this:

container.RegisterType<IControllerConfigurationService>(new PerResolveLifetimeManager(),
    new InjectionFactory(
        x =>
        {
            var bootstrapContext = ((ClaimsIdentity)
                Thread.CurrentPrincipal.Identity).BootstrapContext as BootstrapContext;
            var securityToken = bootstrapContext.SecurityToken;
            return new  ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
                .CreateChannelWithIssuedToken(securityToken);
        }));

Or perhaps better yet, create a class that inherits from ChannelFactory and add a method that pulls that combines the above logic into a single CreateChannelWithIssuedTokenUsingBootstrapContext method:

public class ChannelFactoryWithChannelFactoryOperations<T> : ChannelFactory<T>
{
    protected ChannelFactoryWithChannelFactoryOperations(Type channelType) : base(channelType)
    {
    }

    public ChannelFactoryWithChannelFactoryOperations()
    {
    }

    public ChannelFactoryWithChannelFactoryOperations(string endpointConfigurationName)
        : base(endpointConfigurationName)
    {
    }

    public ChannelFactoryWithChannelFactoryOperations(string endpointConfigurationName,
        EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress)
    {
    }

    public ChannelFactoryWithChannelFactoryOperations(Binding binding) : base(binding)
    {
    }

    public ChannelFactoryWithChannelFactoryOperations(Binding binding, string remoteAddress)
        : base(binding, remoteAddress)
    {
    }

    public ChannelFactoryWithChannelFactoryOperations(Binding binding, EndpointAddress remoteAddress)
        : base(binding, remoteAddress)
    {
    }

    public ChannelFactoryWithChannelFactoryOperations(ServiceEndpoint endpoint) : base(endpoint)
    {
    }

    public T CreateChannelWithIssuedTokenUsingBootstrapContext()
    {
        var bootstrapContext =
            ((ClaimsIdentity) Thread.CurrentPrincipal.Identity).BootstrapContext as BootstrapContext;
        SecurityToken securityToken = bootstrapContext.SecurityToken;
        return CreateChannelWithIssuedToken(securityToken);
    }
}

This allows you to then just call this:

container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
    new InjectionFactory(
    x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
                    .CreateChannelWithIssuedTokenUsingBootstrapContext()));

Solving issue 3: Adding upon the complexity of the above two issues, I'm now trying the same thing outside of IIS in my own Self-Hosted WCF service. This same service is completely stateless, so here's where my next dilemma occurs: Bootstrapping of the security token still occurs, but it isn't occurring on the correct Thread. Unity seems to run its InjectionFactory in a separate Thread to the actual service call execution.

i.e. when the InjectionFactory delegate above executes, the CurrentPrincipal is an unauthorized GenericPrincipal. This is different from what we had in issue 2 above - where it is an authorized ClaimsPrincipal - I believe this is all set up by the IIS session (please do feel free to correct if I'm incorrect).

Funnily enough, if we then replace the above with

container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
    new InjectionFactory(
    x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
                    .CreateChannel()));

i.e. now just inject an unsecured Channel object, we can then observe that in our self-hosted WCF service, where we actually try to interact with the channel, the Thread.CurrentPrincipal is the authenticated ClaimsPrincipal with the SecurityToken correctly Bootstrapped on the principal.

So the problem can be summarized as the following: because the InjectionFactory delegate executes on a thread on which no Authentication/Authorization has yet taken place, the SecurityToken is not actually available to pass to the creation of the channel.

Does anybody have any suggestions on how I might solve this issue? Have I already painted myself into a corner with this particular combination of self-hosted WCF and unity?

Thanks, Clint

Was it helpful?

Solution

I've found a way to do this, though I'd love a better suggestion. The SessionSecurityTokenHandler ValidateToken method always executes on the same Thread as the InjectionFactory delegate, so we can set that Thread's CurrentPrincipal in the ValidateToken method of a CustomSessionSecurityTokenHandler like so:

public class CustomSessionSecurityTokenHandler: SessionSecurityTokenHandler
{
    public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
    {
        var claimsIdentities = base.ValidateToken(token);

        Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentities);

        return claimsIdentities;
    }
}

And then the system.identityModel configuration needs to be modified to include the custom securityTokenHandler:

  <securityTokenHandlers>
    <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
    <add type="MyAssembly.CustomSessionSecurityTokenHandler, MyAssembly" />
  </securityTokenHandlers>

Having done that, attempts to access the security token from the Bootstrap context then succeed:

container.RegisterType<IControllerConfigurationService>(new PerResolveLifetimeManager(),
    new InjectionFactory(
        x =>
        {
            var bootstrapContext = ((ClaimsIdentity)
                 Thread.CurrentPrincipal.Identity).BootstrapContext as BootstrapContext;
            var securityToken = bootstrapContext.SecurityToken;
            return new  ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
                 .CreateChannelWithIssuedToken(securityToken);
        }));

Please feel free to add any alternative suggestions! :)

Thanks, Clint

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top