I have an ASP.NET Core 6 MVC App with Windows authentication. I use UseStatusCodePagesWithReExecute
to handle failure status codes.
When I run the application in Visual studio 2022, it directly goes to the error page due to 401
error. Apparently, the code is execute before Windows authentication occurs. This happens both when anonymous authentication is enabled or disabled.
I tried replacing UseStatusCodePagesWithReExecute
with a middleware , and got the same result.
Here is my program.cs
using Microsoft.AspNetCore.Authentication.Negotiate;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services
.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
//app.UseStatusCodePagesWithReExecute("/StatusCode" , "?statusCode={0}");
//app.UseStatusCodePagesWithReExecute("/Error/{0}");
app.Use(async (context, next) => {
await next();
var status = context.Response.StatusCode;
if (status >= 400)
{
context.Response.Redirect($"/Error/{status}");
}
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Where I have an ErrorController
like this:
[AllowAnonymous]
public class ErrorController : Controller
{
[Route("Error/{statusCode}")]
public IActionResult HandleStatusCode(int statusCode) =>
View("Error");
}
Error
view is in Views->Error. It just contains text.
I may add a switch statement to this middleware to respond to status codes other than 401, but then I won't be able to handle 401 if Windows authentication really fails later.
Thanks to this answer from Json Pan, the following code users 2 middleware for handling non-success status codes, one before app.UseAuthentication();
and app.UseAuthorization();
and another after them. The first middleware excludes 401, and is necessary for handling other status codes, such as 403 which is returned by the authorization middleware and not caught by the middleware which is after authorization.
using Microsoft.AspNetCore.Authentication.Negotiate;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.Use(async (context, next) => {
await next();
var statusCode = context.Response.StatusCode;
if (statusCode >= 400 && statusCode != 401)
{
context.Response.Redirect($"/Error/{statusCode}");
}
});
app.UseHttps
Redirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.Use(async (context, next) => {
var statusCode = context.Response.StatusCode;
if (statusCode >= 400)
{
context.Response.Redirect($"/Error/{statusCode}");
}
await next();
});
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
The code may be modified using app.UseWhen
to distinguish requests to API endpoints and exclude them.