Search code examples
asp.nethangfire

More Complicated Authentication for Hangfire Dashboard


I am using hangfire on an app and I have the need to do dashboard auth using my repositories. To do that, I have to resolve the repository inside Hangfire's Authorize method but, using OwinContext I haven't been able to do so. I chose to use SimpleInjector for this project, and since it registers everything during WebApiConfigs Register method, I want to reach it. Recently I used a middleware as MessageHandler and from that, I successfully resolved a dependency using HttpRequestMessage. But on OwinContext I can't reach it and resolve a dependency through it using HttpRequestMessage.GetDependencyScope().

This is how Hangfire suggests to do the auth for Asp.net apps on their documentation;

public class MyAuthorizationFilter : IDashboardAuthorizationFilter
{
    public bool Authorize(DashboardContext context)
    {
        // In case you need an OWIN context, use the next line, `OwinContext` class
        // is the part of the `Microsoft.Owin` package.
        var owinContext = new OwinContext(context.GetOwinEnvironment());

        // Allow all authenticated users to see the Dashboard (potentially dangerous).
        return owinContext.Authentication.User.Identity.IsAuthenticated;
    }
}

Since I am using Angular on the front-end this owinContext.Authentication.User is null. And even if it wasn't, I only want myself to reach to the dashboard. So this wouldn't solve my problem.

How can I resolve my dependencies here inside Authorize method?

I cannot do it through constructor injection, because for hangfire, you are saying UseHangfireDashboard on the Startup.css Configuration as below states;

This is my Startup.cs file

private IEnumerable<IDisposable> GetHangfireServers()
{
    Hangfire.GlobalConfiguration.Configuration
        .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
        .UseSimpleAssemblyNameTypeSerializer()
        .UseRecommendedSerializerSettings()
        .UseSqlServerStorage("CONN_STR", new SqlServerStorageOptions
        {
            CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
            SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
            QueuePollInterval = TimeSpan.Zero,
            UseRecommendedIsolationLevel = true,
            DisableGlobalLocks = true
        });

    yield return new BackgroundJobServer();
}

public void Configuration(IAppBuilder app)
{
    app.UseHangfireAspNet(GetHangfireServers);
    app.UseHangfireDashboard("/hangfire", new DashboardOptions { 
        Authorization = new [] { 
            new DashboardAuthorization() 
            /* explanation */ 
            // for DI to work through constructor, 
            //I have to give my AuthRepository as a parameter here. 
            //And my AuthRepository also has many DIs so, 
            //it's not possible through here.
        }
    });

    BackgroundJob.Enqueue(() => Console.WriteLine("Hello world from Hangfire!"));

}

My project is a .Net Framework 4.7.2 project by the way.


Solution

  • As far as Hangfire is concerned, you are responsible for providing an instance of the class that implements IDashboardAuthorizationFilter. So if that class has dependencies that you want to inject, that will be on you to wire up. It's probably best to register that type and its dependencies with your DI container, and have it resolve you an instance. That way it takes care of injecting all the dependencies for you via Constructor Injection.

    You should end up with a pattern something like this in your Startup.cs where you configure the OWIN pipeline.

    public void Configuration(IAppBuilder app)
    {
        var container = CreateContainerWithRegisteredServices();
        var dashboardFilter = container.Resolve<IDashboardAuthorizationFilter>();
    
        app.UseHangfireDashboard("/hangfire", new DashboardOptions { 
            Authorization = new [] { dashboardFilter }
        });
    
        BackgroundJob.Enqueue(() => Console.WriteLine("Hello world from Hangfire!"));
    
        // Configure Web API, SignalR, or whatever else hangs off your OWIN pipeline.
        // You can pass the container into their configure methods if necessary.   
    }
    
    IContainer CreateContainerWithRegisteredServices()
    {
        //this method will look different depending on your chosen IoC library
        var container = SomeIoCLibrary.CreateContainer();
        container.Register<MyAuthorizationFilter, IDashboardAuthorizationFilter>();
        //register other dependencies here
    
        return container;
    }