Resolving dependencies in OWIN WEB API Startup.cs with ninject

MrBliz picture MrBliz · Aug 17, 2014 · Viewed 13.1k times · Source

I have a Web Api 2 App, with two classes that both depend on another class, and i'm using ninject to resolve the dependancies.

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    private IUserService _userService;


    public AuthorizationServerProvider(IUserService userService)
    {
        _userService = userService;
    }

}

public class RefreshTokenProvider : IAuthenticationTokenProvider
{
    private IUserService _userService;

    public RefreshTokenProvider(IUserService userService)
    {
        _userService = userService;
    }

In the startup.cs class i need to use the above two classes, but of course i cannot use constructor injection in the startup class, as that is initialised before Ninject is.

What's a way around this so that the references to _tokenProvider and _authServerProvider in the ConfigureAuth method?

public class Startup
{
    private AuthorizationServerProvider _authServerProvider;        
    private RefreshTokenProvider _tokenProvider;

    public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
    public void Configuration(IAppBuilder app)
    {
       var config = new HttpConfiguration();

        app.UseNinjectMiddleware(CreateKernel);
        app.UseNinjectWebApi(config);

        ConfigureOAuth(app);

        WebApiConfig.Register(config);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);

    }

    public void ConfigureOAuth(IAppBuilder app)
    {

        var oAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            AllowInsecureHttp = true, //TODO: HTTPS
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
            Provider = _authServerProvider, 
            RefreshTokenProvider = _tokenProvider
        };

}

Here's the CreateKernel Method

private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        try
        {
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();


            RegisterServices(kernel);
            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }

And here's where I register my services

private static void RegisterServices(IKernel kernel)
    {

        kernel.Bind<SimpleAuthorizationServerProvider>().ToSelf();
        kernel.Bind<SimpleRefreshTokenProvider>().ToSelf();

}

I've followed the advice in the ninject docs, but to no avail.

Answer

Erik Funkenbusch picture Erik Funkenbusch · Aug 24, 2014

You're going about this the wrong way. Well, partially the wrong way anyways. You can't get OWIN to inject dependencies into the Startup class. So, you'll have to use the kernel that gets configured with app.UseNinjectMiddleware() to resolve your options configuration class. We'll do this with a Lazy kernel.

First, you should be configuring this in Startup.Auth.cs. Also, you've got some redundancy in there.. app.UseNinjectWebApi(config) will call app.UseWebApi(config) (see the source). I also don't know why you're calling WebApiConfig.Register() there because that's typically called in Global.asax.cs

In any event, it should look like this (I haven't tested this, but it should be close):

First, we're going to move your kernel creation to a lazy method, and then have your UseNinjectMiddleware() call in Startup.Configuration() method use the lazy kerel in the Startup class as a member. This works best I think as a simple lambda delegate, rather than creating a static CreateKernel method.

public partial class Startup
{
    private readonly Lazy<IKernel> _kernel = new Lazy<IKernel>(() =>
    {
        var kernel = new StandardKernel();

        kernel.Load(Assembly.GetExecutingAssembly());

        // here for brevity, move this to a RegisterServices or similar method,
        // 
        kernel.Bind<IOAuthAuthorizationServerOptions>()
            .To<MyOAuthAuthorizationServerOptions>();
        kernel.Bind<IOAuthAuthorizationServerProvider>()
            .To<AuthorizationServerProvider>();
        kernel.Bind<IAuthenticationTokenProvider>().To<RefreshTokenProvider>();
        kernel.Bind<IUserService>().To<MyUserService>();
        return kernel;
    });

    public void Configuration(IAppBuilder app)
    {
        app.UseNinjectMiddleware(() => _kernel.Value);
        var config = new HttpConfiguration();
        app.UseNinjectWebApi(config);

        ConfigureAuth(app);
    }
}

Then in your ConfigureAuth()

public void ConfigureAuth(IAppBuilder app)
{
   // .... other auth code

   // Yes, boo hiss, service location, not much choice...
   // Setup Authorization Server
   app.UseOAuthAuthorizationServer(_kernel.Value
       .Get<MyOAuthAuthorizationServerOptions>().GetOptions());
}