Using Hangfire Dashboard in ABP API Website 🚀
Introduction
In this article, I'll show you how to integrate and use the Hangfire Dashboard in an ABP API website.
Typically, API websites use JWT Bearer
authentication, but the Hangfire Dashboard isn't compatible with JWT Bearer
authentication. Therefore, we need to implement Cookies
and OpenIdConnect
authentication for the Hangfire Dashboard access.
Creating a New ABP Demo Project 🛠️
We'll create a new ABP Demo Tiered
project that includes AuthServer
, API
, and Web
projects.
abp new AbpHangfireDemoApp -t app --tiered
Now let's add the Hangfire Dashboard to the API
project and configure it to use Cookies
and OpenIdConnect
authentication for accessing the dashboard.
Adding a New Hangfire Application 🔧
We need to add a new Hangfire application to the appsettings.json
file in the DbMigrator
project:
Note: Replace
44371
with yourAPI
project's port.
"OpenIddict": {
"Applications": {
//...
"AbpHangfireDemoApp_Hangfire": {
"ClientId": "AbpHangfireDemoApp_Hangfire",
"RootUrl": "https://localhost:44371/"
}
//...
}
}
- Update the
OpenIddictDataSeedContributor
'sCreateApplicationsAsync
method in theDomain
project to seed the new Hangfire application.
//Hangfire Client
var hangfireClientId = configurationSection["AbpHangfireDemoApp_Hangfire:ClientId"];
if (!hangfireClientId.IsNullOrWhiteSpace())
{
var hangfireClientRootUrl = configurationSection["AbpHangfireDemoApp_Hangfire:RootUrl"]!.EnsureEndsWith('/');
await CreateApplicationAsync(
applicationType: OpenIddictConstants.ApplicationTypes.Web,
name: hangfireClientId!,
type: OpenIddictConstants.ClientTypes.Confidential,
consentType: OpenIddictConstants.ConsentTypes.Implicit,
displayName: "Hangfire Application",
secret: configurationSection["AbpHangfireDemoApp_Hangfire:ClientSecret"] ?? "1q2w3e*",
grantTypes: new List<string> //Hybrid flow
{
OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit
},
scopes: commonScopes,
redirectUris: new List<string> { $"{hangfireClientRootUrl}signin-oidc" },
postLogoutRedirectUris: new List<string> { $"{hangfireClientRootUrl}signout-callback-oidc" },
clientUri: hangfireClientRootUrl,
logoUri: "/images/clients/aspnetcore.svg"
);
}
- Run the
DbMigrator
project to seed the new Hangfire application.
Adding Hangfire Dashboard to the API
Project 📦
- Add the following packages and modules dependencies to the
API
project:
<PackageReference Include="Volo.Abp.BackgroundJobs.HangFire" Version="9.2.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.OpenIdConnect" Version="9.2.0" />
<PackageReference Include="Hangfire.SqlServer" Version="1.8.20" />
typeof(AbpBackgroundJobsHangfireModule),
typeof(AbpAspNetCoreAuthenticationOpenIdConnectModule)
- Add the
HangfireClientId
andHangfireClientSecret
to theappsettings.json
file in theAPI
project:
"AuthServer": {
"Authority": "https://localhost:44358",
"RequireHttpsMetadata": true,
"MetaAddress": "https://localhost:44358",
"SwaggerClientId": "AbpHangfireDemoApp_Swagger",
"HangfireClientId": "AbpHangfireDemoApp_Hangfire",
"HangfireClientSecret": "1q2w3e*"
}
- Add the
ConfigureHangfire
method to theAPI
project to configure Hangfire:
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
//...
//Add Hangfire
ConfigureHangfire(context, configuration);
//...
}
private void ConfigureHangfire(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddHangfire(config =>
{
config.UseSqlServerStorage(configuration.GetConnectionString("Default"));
});
}
- Modify the
ConfigureAuthentication
method to add newCookies
andOpenIdConnect
authentication schemes:
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddAbpJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = configuration.GetValue<bool>("AuthServer:RequireHttpsMetadata");
options.Audience = "AbpHangfireDemoApp";
options.ForwardDefaultSelector = httpContext => httpContext.Request.Path.StartsWithSegments("/hangfire", StringComparison.OrdinalIgnoreCase)
? CookieAuthenticationDefaults.AuthenticationScheme
: null;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddAbpOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
options.ResponseType = OpenIdConnectResponseType.Code;
options.ClientId = configuration["AuthServer:HangfireClientId"];
options.ClientSecret = configuration["AuthServer:HangfireClientSecret"];
options.UsePkce = true;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("roles");
options.Scope.Add("email");
options.Scope.Add("phone");
options.Scope.Add("AbpHangfireDemoApp");
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
//...
}
- Add a custom middleware and
UseAbpHangfireDashboard
afterUseAuthorization
in theOnApplicationInitialization
method:
//...
app.UseAuthorization();
app.Use(async (httpContext, next) =>
{
if (httpContext.Request.Path.StartsWithSegments("/hangfire", StringComparison.OrdinalIgnoreCase))
{
var authenticateResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
if (!authenticateResult.Succeeded)
{
await httpContext.ChallengeAsync(
OpenIdConnectDefaults.AuthenticationScheme,
new AuthenticationProperties
{
RedirectUri = httpContext.Request.Path + httpContext.Request.QueryString
});
return;
}
}
await next.Invoke();
});
app.UseAbpHangfireDashboard("/hangfire", options =>
{
options.AsyncAuthorization = new[]
{
new AbpHangfireAuthorizationFilter()
};
});
//...
Perfect! 🎉 Now you can run the AuthServer
and API
projects and access the Hangfire Dashboard at https://localhost:44371/hangfire
.
Note: Replace
44371
with yourAPI
project's port.
The first time you access the Hangfire Dashboard, you'll be redirected to the login page of the AuthServer
project. After you log in, you'll be redirected back to the Hangfire Dashboard.
Key Points 🔑
1. Authentication Scheme Selection
The default authentication scheme in API websites is JWT Bearer
. We've implemented Cookies
and OpenIdConnect
specifically for the Hangfire Dashboard.
We've configured the JwtBearerOptions
's ForwardDefaultSelector
to use CookieAuthenticationDefaults.AuthenticationScheme
for Hangfire Dashboard requests.
This means that if the request path starts with /hangfire
, the request will be authenticated using the Cookies
authentication scheme; otherwise, it will use the JwtBearer
authentication scheme.
options.ForwardDefaultSelector = httpContext => httpContext.Request.Path.StartsWithSegments("/hangfire", StringComparison.OrdinalIgnoreCase)
? CookieAuthenticationDefaults.AuthenticationScheme
: null;
2. Custom Middleware for Authentication
We've also implemented a custom middleware to handle Cookies
authentication for the Hangfire Dashboard. If the current request isn't authenticated with the Cookies
authentication scheme, it will be redirected to the login page.
app.Use(async (httpContext, next) =>
{
if (httpContext.Request.Path.StartsWithSegments("/hangfire", StringComparison.OrdinalIgnoreCase))
{
var authenticateResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
if (!authenticateResult.Succeeded)
{
await httpContext.ChallengeAsync(
OpenIdConnectDefaults.AuthenticationScheme,
new AuthenticationProperties
{
RedirectUri = httpContext.Request.Path + httpContext.Request.QueryString
});
return;
}
}
await next.Invoke();
});
Comments
NH Support NH Support 2 weeks ago
Yes working with me, I was facing an issue before this article thanks
Liming Ma 2 weeks ago
Thanks : )