Registering OWIN IAuthenticationManager using Castle Windsor

1.1k Views Asked by At

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?

2

There are 2 best solutions below

9
Matías Fidemraizer On BEST ANSWER

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.

2
bounav On

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())