Some preamble information:
- The solution I have is for implementations that follow the MSAL 2 flows, using .NET Core 6.
- For the statement:
Default authentication is JWTBearerDefaults, but for the Hangfire
Dashboard I need AzureAdDefaults
AzureAdDefaults is now obsolete, so you would need to use something else, like the JWTBearerDefaults, but read on to see how I got my implementation working.
- Another confusing thing for me was having a WebApi which also acted as a WebApp, since you're enabling application-like authentication on certain endpoints, but on others, not. Turns out this is possible.
Solution:
I came across this nugget after quite a lot of browsing through code samples:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
services.AddAuthentication()
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"),
JwtBearerDefaults.AuthenticationScheme)
.EnableTokenAcquisitionToCallDownstreamApi();
Essentially, this is saying you can have 2 separate calls to .AddAuthentication(...), with 1 chaining the AddMicrosoftIdentityWebApp call, and another the AddMicrosoftIdentityWebApi call, specifying which authentication to use in both cases.
For me, I setup my Api to authenticate with JwtBearerDefaults.AuthenticationScheme, and the WebApp portion uses OpenIdConnectDefaults.AuthenticationScheme, making it look like this:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"),
JwtBearerDefaults.AuthenticationScheme);
services.AddAuthentication()
.AddMicrosoftIdentityWebApp(Configuration, "AzureAd", OpenIdConnectDefaults.AuthenticationScheme);
Next, setup a policy for Hangfire to use, which matches the scheme previously setup for the WebApp portion.
services.AddAuthorization(options =>
{
options.AddPolicy("azureAdPolicy", builder =>
{
builder
.AddAuthenticationSchemes(OpenIdConnectDefaults.AuthenticationScheme)
.RequireAuthenticatedUser();
});
});
I then created some extension methods for setting up the Hangfire services and routes.
public static IServiceCollection AddHangfireService(
this IServiceCollection services,
IConfiguration configuration)
{
services.AddHangfireServer();
services.AddHangfire(cfg =>
{
// Additional setup code
});
return services;
}
Called like so:
services.AddHangfireService(Configuration);
For the routing:
public static IEndpointRouteBuilder AddHangfireRoute(this IEndpointRouteBuilder endpoints,
HangfireDBOptions hangfireDbOptions)
{
endpoints.MapHangfireDashboard("/hangfire", new DashboardOptions
{
DashboardTitle = "Your App Title",
AppPath = hangfireDbOptions.BackToSiteUrl,
Authorization = new[]
{
new HangfireDashboardAuthFilter() // if you need a filter
}
})
.RequireAuthorization("azureAdPolicy"); // Matches your policy name
return endpoints;
}
Which is called like so:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // Call this first
endpoints.AddHangfireRoute(hangfireOptions);
});
The only thing left to do is properly configure the routes for your Hangfire routes in your Azure AD environment.
On your App Registration, add a Web application type, and add the following routes:
- (your base application URL)/signin-oidc
- (your base application URL)
- (your base application URL)/hangfire
And make sure to have the ID Token checkbox selected

That should be it :) Hope this helps, your application might be using other auth flows or older library versions etc - this is what I got working which is currently used in production. I would recommend you upgrade your packages anyway as there are a lot of 'better' auth flows to use, and as I mentioned, some features are now obsolete.
Humble apologies again for the delayed answer, was quite a lot of information to compile