Hangfire: Token Authentication

Created on 11 Apr 2017  路  10Comments  路  Source: HangfireIO/Hangfire

I am using hangfire as part of a site that uses token based authentication. How can I secure the hangfire dashboard with token authentication?

Most helpful comment

I can just add to all of this: yes, its not Hangfire responsibility to authenticate, but it still has responsibility to send authentication information in the way it is expected at server side.
If service is using cookies to store and send that information - then all seems to be fine and it works well.
However if service expects authentication information in 'Authorization' header, than its an issue, as there is no way to inject into Hangfire UI header information.

Maybe you can take a look how it is done in swagger: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-definitions-object
(and it also has some .NET implementations arround this like Swashbuckle for example).

So please add some support for OAuth2 authorization flows.

All 10 comments

@burningice2866 Link you have posted only provide an example of cookie-based authentication. I can't see an example of token-based authentication.

@johnmay1

This example should apply to any form of authentication you are using on the primary site... as long as you implement the Authorize method of the IDashboardAuthorizationFilter and return true or false.

If you are running the hangfire UI on a totally different site by itself, then I think you'd have to still implement a minimal primary site to support whatever login/authentication mechanism you have, and then reference that from hangfire.

@dcorbett-fivebridgesllc In token authentication you have to pass "auth_token" into request header and that is missing from hangfire UI context request header.

@johnmay1 you said it yourself, token authentication. Its not Hangfires job to authenticate, only authorize based on the identity who's authenticated.

There is nothing in the link i provided that says cookie. All it shows is how to _authorize_ based on who is _authenticated_. In this case its a simple logic stating that all who's authenticated is automatically authorized but it could be more sophisticated based on roles or claims.

@burningice2866 To take advantage of the ASP.NET infrastructure around tokens, I believe, all of the web requests back to ASP.NET need to have the token passed in as part of the header.

Every request from Hangfire would need to include the token in the headers...doing something like:
headers.append('Authorization', `Bearer ${token}`);

A possible workflow, although I do not know how feasible this is:

  • Developer to get the token to Hangfire, possibly with a query string via the first call to Hangfire, i.e localhost\hangfire?token=reallyLongTokenString

  • Hangfire 'hangs onto' the token and sends it along to the backend on every request.

  • Developer then creates their own IDashboardAuthorizationFilter, grab the user from context.GetHttpContext().User and performs permissions evaluation to return true or false.

I can just add to all of this: yes, its not Hangfire responsibility to authenticate, but it still has responsibility to send authentication information in the way it is expected at server side.
If service is using cookies to store and send that information - then all seems to be fine and it works well.
However if service expects authentication information in 'Authorization' header, than its an issue, as there is no way to inject into Hangfire UI header information.

Maybe you can take a look how it is done in swagger: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-definitions-object
(and it also has some .NET implementations arround this like Swashbuckle for example).

So please add some support for OAuth2 authorization flows.

I know this is an old issue, but if anyone lands here looking for a solution, this is how I got it working:

  • Allowed token to be passed as query string parameter with Identity Server authentication;
  • Created a IDashboardAuthorizationFilter to allow authenticated users;
  • Created a Tampermonkey script to intercept the dashboard requests.

Allow Query String Parameter Access Token

DISCLAIMER: Be careful! Exposing the access token via query string is potentially dangerous. Do so at your own risk.

First, you need to allow tokens coming from the query string. I did so by changing my Startup.ConfigureServices as below:

services.AddAuthentication("Bearer")
    .AddIdentityServerAuthentication(options =>
    {
        //other options omitted for brevity
        var fromQueryString = TokenRetrieval.FromQueryString();
        var fromAuthorizationHeader = TokenRetrieval.FromAuthorizationHeader();
        options.TokenRetriever = request => fromQueryString(request) ?? fromAuthorizationHeader(request);
    });

Remeber to put app.UseAuthentication() before app.UseHangfireDashboard() in your Startup.Configure.

Authorization Filter

DISCLAIMER: The filter I've created allow ANY authenticated user, but that is not recommneded at all. However, you can do any type of validation at your own flavor, like roles, special permissions, etc. So, go crazy protecting your data.

I created a filter to simply allow authenticated users:

public class HangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter
{
    public bool Authorize(DashboardContext context)
    {
        var user = context.GetHttpContext().User;
        return user.Identity.IsAuthenticated;
    }
}

Then added it to app.UseHangfireDashboard() in Startup.Configure:

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[] { new HangfireDashboardAuthorizationFilter() },
    IgnoreAntiforgeryToken = true
});

The IgnoreAntiforgeryToken part is necessary to run successfully on a remote server farm.

Tampermonkey Script

DISCLAIMER: You can download the script, modify it, use it, freely. No strings attached. But do it at your own risk.

To inject the authorization in the dashboard page, I created a Tampermonkey script (available here).

The script has its issues, like being unable to add the access token to addresses inside the css files url() (like woff fonts) but works well enough.

What It Does?

The script does 3 things:

  1. Stores the access token using localStorage;
  2. Add a query string parameter access_token=token to all elements in the page with src or href attributes;
  3. Intercept every XMLHttpRequest to inject the token in the header;
  4. Reload the script tags from the body into the head (ATM was the only way to include the access token and load js contents)

Right now, the script matches everything (any path in browser) to allow you to use it in a more flexible way.

How to Use It

  1. Install Tampermonkey (or another user script manager for your browser);
  2. Add the script, available raw as gist here;
  3. Navigate anywhere. If you allowed the script to match anything, just open Tampermonkey menu to see the the script's items. It should look as follows:

Tampermonkey Menu

The Menus

Open New Window With Access Token

Allows you to pass in the token and the dashboard URL. It then opens a new tab with the access token as a query string parameter.
Instead of using this menu, you can also just navigate to the dashboard page using a URL with a valid ?access_token=token parameter. The script will read it, enable the injections and continue.

Enable Token Injection

Allows you to pass in the token. It then make the replacements and start injecting the token without reloading the page.

Disable Token Injection

Disables the injection by removing the token from the localStorage and reverts the addresses in the page, without reloading it.

Conclusion

I made this as an attempt to fill a necessity for my project and wanted to share it with you all. Perhaps I can help someone going through the same thing.
Also, as I said in the disclaimers, do any of this at your own risk.

Cheers,

Cleverson Nascimento

Still no support to add original query string to dashboard links?
Will be good to forward original headers or add least original query string to allow token forwarding

I think this could be done pretty easily by adding a hangfire config value to specify where in local storage to get the token for the bearer authorization token.

It is really easy to attach that to the ajax requests.

For the initial page load it wouldn't be helpful, but that is a different problem w/ header token auth.

You can use a signed cookie to identify someone that is using header auth tokens to decide whether or not to give access to the page and then you can require the actual access token for the ajax requests.

If the page itself is not completely secured by the access token you do run the risk of leaking infrastructure information, but the data is the only thing that HAS to be secured.

       <div id="hangfireConfig"
             data-pollinterval="@DashboardOptions.StatsPollingInterval"
             data-pollurl="@(Url.To("/stats"))">
             data-bearerpath="blah"
        </div>

Another solution would just be to allow the configuration of an additional js file to be loaded. You would then be able to intercept and customize the ajax requests made by the hangfire dashboard.

Was this page helpful?
0 / 5 - 0 ratings