Hi,
I setup an oidc implementation ages ago for a hangfire dashboard I have, but never really used it. Anyway I’ve picked up again, upgraded the project to .net 6 and reworked a few bits and bobs, but I’m having a weird issue where by it works with oidc locally, but when I deploy it to my server and access remotely, it flows through the auth fine and redirects back to the signedIn controller, but then when it redirects with a fully authenticated user, it returns a 403.
This is my DashboardAuthorizationFilter and the auth startup config
internal interface IAuthenticationProvider
{
IDashboardAuthorizationFilter GetAuthorizationFilter();
}
internal class AuthenticationProvider : IAuthenticationProvider
{
private readonly IConfigurationFactory _configurationFactory;
public AuthenticationProvider(IConfigurationFactory configurationFactory)
{
_configurationFactory = configurationFactory;
}
public IDashboardAuthorizationFilter GetAuthorizationFilter()
{
return _configurationFactory.GetConfig().EnableSso
? new IdentityServerAuthorizationFilter() as IDashboardAuthorizationFilter
: new LocalRequestsOnlyAuthorizationFilter();
}
}
internal static class AuthenticationServiceProvider
{
public static IServiceCollection Register([NotNull] this IServiceCollection services, HostConfiguration configuration)
{
if (configuration.EnableSso)
{
services
.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, opt =>
{
opt.ExpireTimeSpan = TimeSpan.FromMinutes(60);
opt.Cookie.Name = "scheduler";
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, opt =>
{
opt.Authority = configuration.AuthServiceUrl;
opt.RequireHttpsMetadata = false;
opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
opt.ClientId = "scheduler_client";
opt.ClientSecret = "secret";
opt.ResponseType = "code id_token";
opt.CallbackPath = "/SignedIn";
opt.Scope.Clear();
opt.Scope.Add("openid");
opt.Scope.Add("profile");
opt.Scope.Add("offline_access");
opt.Scope.Add("scheduler");
opt.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce", "iat", "c_hash");
opt.GetClaimsFromUserInfoEndpoint = true;
opt.SaveTokens = true;
opt.Events.OnRedirectToIdentityProvider = async context =>
{
if (context.Properties.Items.ContainsKey("idpName"))
context.ProtocolMessage.SetParameter("idpName", context.Properties.Items["idpName"]);
await Task.CompletedTask;
};
});
}
return services;
}
}
Here is an excert from my startup
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<IAuthenticationProvider, AuthenticationProvider>();
services.AddMvc();
AuthenticationServiceProvider.Register(services, HostConfiguration);
services.AddHangfire((sp, config) =>
{
config.UseSqlServerStorage(() => sp.GetService<IDbConnectionFactory>().Create() as DbConnection);
config.UseAtoms();
config.UseSerilogLogProvider();
});
if (HostConfiguration.Mode == OperationMode.AllInOne || HostConfiguration.Mode == OperationMode.Server)
{
services.AddHangfireServer(options =>
{
options.ServerCheckInterval = TimeSpan.FromSeconds(10);
options.HeartbeatInterval = TimeSpan.FromSeconds(10);
options.ServerTimeout = TimeSpan.FromSeconds(15);
options.WorkerCount = Environment.ProcessorCount * HostConfiguration.WorkerCountPerCore;
options.ServerName = "Scheduler";
options.Queues = new[] { "default" };
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
IRecurringJobManager recurringJobManager,
IConfigurationFactory configurationFactory)
{
var config = configurationFactory.GetConfig();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.Use(async (context, next) =>
{
await next.Invoke();
if(context.Response.StatusCode == StatusCodes.Status401Unauthorized && config.EnableSso)
{
context.Response.Redirect(config.WebAppUrl.AppendToRelativePath("scheduler"));
}
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHangfireDashboard();
});
if (config.Mode == OperationMode.AllInOne || config.Mode == OperationMode.Dashboard)
{
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
AppPath = config.WebAppUrl.GetRelativePath(),
Authorization = new List<IDashboardAuthorizationFilter>
{
app.ApplicationServices.GetRequiredService<IAuthenticationProvider>().GetAuthorizationFilter()
},
StatsPollingInterval = 1000
});
}
}
It feels like something is overriding the IDashboardAuthorizationFilter or something to do with the routing, but I haven’t managed to put my finger on it.
I have confirmed that the HttpContext.Identity.User.IsAuthenticated and does have the role “Administrator” as specified in my auth filter.