Dashboard: handle GET /logout (was 405) by signing out + redirecting to /login
Browsers that navigate directly to /logout via the address bar issued a GET against a POST-only route and got 405 Method Not Allowed. Logout is self-destructive, so the GET path can skip antiforgery; the existing POST form (used by the layout's Sign out button) is unchanged and still antiforgery-protected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,15 @@ public static class DashboardEndpointRouteBuilderExtensions
|
||||
.AllowAnonymous()
|
||||
.WithName("DashboardLogout");
|
||||
|
||||
// Browsers that navigate directly to /logout issue a GET. Sign out and
|
||||
// redirect to /login so the URL works as users expect. Logout is
|
||||
// self-destructive, so the GET path intentionally skips antiforgery.
|
||||
endpoints.MapGet(
|
||||
"/logout",
|
||||
(Delegate)(static (HttpContext httpContext) => GetLogoutAsync(httpContext)))
|
||||
.AllowAnonymous()
|
||||
.WithName("DashboardLogoutGet");
|
||||
|
||||
endpoints.MapGet("/denied", () => Results.Content(
|
||||
RenderPage("Access denied", "<p>The signed-in user is not authorized for dashboard access.</p>"),
|
||||
"text/html"))
|
||||
@@ -140,6 +149,15 @@ public static class DashboardEndpointRouteBuilderExtensions
|
||||
return Results.LocalRedirect("/login");
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetLogoutAsync(HttpContext httpContext)
|
||||
{
|
||||
await httpContext
|
||||
.SignOutAsync(DashboardAuthenticationDefaults.AuthenticationScheme)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return Results.LocalRedirect("/login");
|
||||
}
|
||||
|
||||
private static string RenderLoginPage(
|
||||
HttpContext httpContext,
|
||||
IAntiforgery antiforgery,
|
||||
|
||||
@@ -63,7 +63,7 @@ public sealed class GatewayApplicationTests
|
||||
IReadOnlyList<RouteEndpoint> endpoints = GetRouteEndpoints(app);
|
||||
|
||||
string[] anonymousEndpointNames =
|
||||
["DashboardLogin", "DashboardLoginPost", "DashboardLogout", "DashboardAccessDenied"];
|
||||
["DashboardLogin", "DashboardLoginPost", "DashboardLogout", "DashboardLogoutGet", "DashboardAccessDenied"];
|
||||
foreach (string endpointName in anonymousEndpointNames)
|
||||
{
|
||||
RouteEndpoint endpoint = Assert.Single(
|
||||
|
||||
Reference in New Issue
Block a user