Securing Hangfire Dashboard in ASP.NET Core with a Custom Auth Policy
Tags: hangfire, dotnetcore, authorization, csharp
Published: 2020-05-16 11:46:09

---
title: Securing Hangfire Dashboard in ASP.NET Core with a Custom Auth Policy
published: true
date: 2020-05-16 11:48:00 UTC
tags: hangfire, dotnetcore, authorization, csharp
cover_image: https://sahansera.dev/static/fb1517c25fdc09dc0a7bddd25dd36f1a/dcbba/hangfire-aspnetcore.webp
canonical_url: https://sahansera.dev/securing-hangfire-dashboard-with-endpoint-routing-auth-policy-aspnetcore/
---

This blog post assumes that you have a basic understanding of Hangfire. If not, you can follow their excellent [guide](https://docs.hangfire.io/en/latest/) on how to get started. The scope of this blog post is to show you how to secure the Hangfire Dashboard and only authorise certain users to be able to access it without opening it up to the general public.

By default, if you try to access your Hangfire dashboard, it will work perfectly fine on your `localhost` because local requests are allowed. However, things can get a bit tricky when you want to secure your Dashboard.

For starters, Hangfire provides us with a [`IDashboardAuthorizationFilter`](https://docs.hangfire.io/en/latest/configuration/using-dashboard.html) to perform our custom authorisation when deployed. But, what if we want to do the authorisation at an endpoint level rather than using a custom filter?

### Step 1 - Initial Setup

To get started, you can clone the repo I have put together:

[Securing Hangfire Dashboard](https://github.com/sahan91/securing-hangfire-dashboard)

To summarise, the above solution is a .NET Core 3.1 Web API project and has the following dependencies.

![./hangfire-aspnetcore-1.png](https://sahansera.dev/static/df848ee13bd4dbea4accfc08a929e68e/11d19/hangfire-aspnetcore-1.png)

We use MemoryStorage since we don’t want to persist anything for this demo. This will work just fine with SQLStorage. To keep things simple on the authentication side of things, I have used AzureAD.UI library.

Once you have cloned the repo, do not forget to create an app registration in the Azure portal, under Active Directory. Make sure you have set the correct Redirect URIs (Note: we need to add `hangfire` URI) and selected ID tokens.

![./hangfire-aspnetcore-2.png](https://sahansera.dev/static/426b73dcb47cd0d47277e367632824c0/11d19/hangfire-aspnetcore-2.png)

You also need to copy and paste the `ClientId` and `TenantId` from your app registration to `appsettings.json`

```json
"AzureAd": {
  "ClientId": "Enter_the_Application_Id_here",
  "TenantId": "Enter_the_Tenant_Info_Here"
}
```

Once you have done this setup, you can just press F5 and head over to [`https://localhost:44317/hangfire`](https://localhost:44317/hangfire) to see if everything is working.

### Step 2 - Adding a custom authorisation policy

For our server to tell which policy to use to authorise users to access the Hangfire dashboard, we need to define a custom authorisation policy.

[Startup.cs](https://github.com/sahan91/securing-hangfire-dashboard/blob/master/SecuringHangfireDashboard/SecuringHangfireDashboard/Startup.cs)

```csharp
public void ConfigureServices(IServiceCollection services)
{
    // Add Authentication
    
    ...Code removed for brevity

    // Add a new policy for hangfire
    services.AddAuthorization(options =>
    {
        // Policy to be applied to hangfire endpoint
        options.AddPolicy(HangfirePolicyName, builder =>
        {
            builder
                .AddAuthenticationSchemes(AzureADDefaults.AuthenticationScheme)
                .RequireAuthenticatedUser();
        });
    });

    ...Code removed for brevity

    // Bootstrap Hangfire
    services.AddHangfire(configuration => configuration
        .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
        .UseSimpleAssemblyNameTypeSerializer()
        .UseRecommendedSerializerSettings()
        .UseMemoryStorage());
}
```

### Step 3 - Securing the Dashboard

Now that we have defined our custom authorisation policy, let’s configure our Hangfire dashboard endpoint to require authorisation with our `HangfirePolicyName` policy.

```csharp
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...Code removed for brevity

    // Hangfire Settings
    app.UseHangfireServer();
    app.UseHangfireDashboard();

    // Configure endpoints
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapHangfireDashboard("/hangfire", new DashboardOptions()
        {
            Authorization = new List<IDashboardAuthorizationFilter> { }
        })
        .RequireAuthorization(HangfirePolicyName);
    });

    //Register our background job
    RecurringJob.AddOrUpdate("some-id", () => Console.WriteLine(), Cron.Minutely);
}
```

It is important to remember that we need to pass in an empty list of `IDashboardAuthorizationFilter` because otherwise, Hangfire will block all your requests to the dashboard when deployed (i.e. works only on `localhost` if we don’t override it like this).

Now head over to [`https://localhost:44317/hangfire`](https://localhost:44317/hangfire) you will be asked to login since our new hangfire dashboard endpoint is secured with Azure AD. Once you log in, you will see the dashboard.

![./hangfire-aspnetcore-3.png](https://sahansera.dev/static/3bfc9ed45e360dfd36a1d276cd09c606/11d19/hangfire-aspnetcore-3.png)

### Step 4 - Requiring a Specific Role

We can also add custom role checks inside our `AddPolicy()` call. This is quite useful and a more secure way to protect your Hangfire Dashboard endpoint rather than granting access to all authenticated users of your system.

```csharp
...
services.AddAuthorization(options =>
    {
      // Policy to be applied to hangfire endpoint
      options.AddPolicy(HangfirePolicyName, builder =>
      {
        builder
          .AddAuthenticationSchemes(AzureADDefaults.AuthenticationScheme)
          .RequireAuthenticatedUser();

        // Your custom role check
        if (env.IsProduction())
        {
          builder.RequireRole("AdministratorRoleName");
        }
      });
    });
...
```

### Conclusion

With this approach, you can have a nicely decoupled way of protecting your Hangfire dashboard route. You can also move the authorisation logic to a custom extension method and inject your custom services as opposed to using a Hangfire’s authorisation filter.

There’s a [pull request in Hangfire repository](https://github.com/HangfireIO/Hangfire/pull/1663) to add an extension method to add authorisation policy support by a colleague of mine. Feel free to upvote it 🙂

### References

1. [https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-3.1](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-3.1)
2. [https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-webapp#option-2-register-and-manually-configure-your-application-and-code-sample](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-webapp#option-2-register-and-manually-configure-your-application-and-code-sample)
3. [https://docs.hangfire.io/en/latest/configuration/using-dashboard.html](https://docs.hangfire.io/en/latest/configuration/using-dashboard.html)