3

Since IAuthenticationManager implementation can be retrieved from OWIN context, but Castle Windsor's component registration must be done before resolving components, how can I register IAuthenticationManager as component to get injected anywhere?

AFAIK, I should use Component.For<IAuthenticationManager>().UsingFactoryMethod(...), but since I'm using OWIN/Katana, something like HttpContext.Current.GetOwinContext() won't work (and if it would work, I would hate to add a dependency to System.Web for this...).

What's the solution for this right now?

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206

2 Answers2

2

Temporal (or definitive) solution...

This is how I've managed to solve the issue.

First of all, I've implemented a simple OWIN middleware:

public sealed class WindsorMiddleware : OwinMiddleware
{
    public WindsorMiddleware(OwinMiddleware next) : base(next)
    {
    }

    public override async Task Invoke(IOwinContext context)
    {
        CallContext.LogicalSetData("owinContext", context);

        await Next.Invoke(context);

        CallContext.FreeNamedDataSlot("owinContext");
    }
}

And I've configured IAuthenticationManager using ComponentRegistration<T>.UseFactoryMethod so I've implemented an extension method like this:

public static ComponentRegistration<TService> UseOwinComponentFactoryMethod<TService>(this ComponentRegistration<TService> registration)
    where TService : class
{
    return registration.UsingFactoryMethod
    (
        (kernel, componentModel, creationContext) =>
        {
            IOwinContext owinContext = CallContext.LogicalGetData("owinContext") as IOwinContext;

            Contract.Assert(owinContext != null);

            if (creationContext.RequestedType == typeof(IAuthenticationManager))
            {
                return (TService)owinContext.Authentication;
            }
            else
            {
                throw new NotSupportedException();
            }
        },
        managedExternally: true
    );
}

Finally, I've registered IAuthenticationManager this way:

Component.For<IAuthenticationManager>().UseOwinComponentFactoryMethod().LifestyleTransient()

It smells...

BTW, I'm not self-convinced about the reliability of this solution since this should work unless you try to resolve components in another thread than the request one.

Sadly, it should be a lot of situations where this solution can fail. If your code implements non-blocking I/O, I expect to try to inject IAuthenticationManager from another thread from the one that set "owinContext" in the CallContext...

I'll still look forward for other answers while I find a better and more elegant solution.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • You might want to add `managedExternally: true` parameter of UsingFactoryMethod() just to be clear that Windsor is not responsible for applying any commission/decommission concerns. – Phil Degenhardt Aug 06 '15 at 00:48
  • It seems the DI story in Katana is fairly poor. It is getting an overhaul in ASP.NET 5 which should improve things: http://www.emadashi.com/2015/06/dependency-injection-in-asp-net-5-one-step-deeper/ – Phil Degenhardt Aug 06 '15 at 00:50
  • @PhilDegenhardt Thanks for the suggestion – Matías Fidemraizer Aug 06 '15 at 07:14
  • Interesting. I didn't know about the [CallContext](https://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext(v=vs.110).aspx) class. It's been over a year now. Are you still using this implementation to access your OWIN context from Windsor Castle? – bounav Nov 18 '16 at 12:23
  • @MatíasFidemraizer I tried your code but when the UseOwinComponentFactoryMethod() extension method executes, `IOwinContext owinContext = CallContext.LogicalGetData("owinContext") as IOwinContext;` return null. I am registering the `WindsorMiddleWare` in Startup.cs like so: `app.Use(typeof(WindsorMiddleware));`. – bounav Nov 18 '16 at 13:51
  • @bounav Uhm, it's strange. I'm sure it works because I'm still developing the same project and I've more than one OWIN/Katana-hosted WebAPI using this approach. It might be something different in your actual code than mine... – Matías Fidemraizer Nov 18 '16 at 20:17
  • @bounav Oh, I've an idea. Did you know that middleware registration order matters? I mean, register this middleware before any other one and see what happens. – Matías Fidemraizer Nov 18 '16 at 20:18
  • @MatíasFidemraizer I tried calling `app.Use();` before or after `ConfigureAuth(app);` but it doesn't make any difference, the owin context isn't in CallContext... However, I'm using ASP.NET MVC 5, not web API, maybe that why it doesn't work? – bounav Nov 19 '16 at 12:49
  • @bounav Who knows, also I'm using OWIN/Katana. Maybe you're hosting your app on top of IIS which works with another threading model... – Matías Fidemraizer Nov 19 '16 at 13:03
0

For those who don't mind having a dependency to System.Web, the following code should work (and it doesn't require a middleware).

private static IAuthenticationManager GetAuthenticationManager(IKernel kernel, ComponentModel componentModel, CreationContext creationContext)
{
    var owinContext = new HttpContextWrapper(HttpContext.Current).GetOwinContext();

    return owinContext.Authentication;
}

Then in your castle windsor installer:

container.Register(Component.For<IAuthenticationManager>()
                            .UsingFactoryMethod(GetAuthenticationManager, managedExternally: true)
                            .LifestyleTransient())
bounav
  • 4,886
  • 4
  • 28
  • 33