e0a3fbf35b
The themed Blazor <LoginCard> page (Components/Pages/Login.razor, @page "/login")
registers a Razor Components endpoint that matches ALL HTTP methods. The credential
form POSTed to /login, where MapPost("/login") also matched — so every POST /login
threw Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException (HTTP 500),
breaking dashboard login for every user. It was latent because the dashboard was only
ever reached via the AllowAnonymousLocalhost bypass on the host box.
Move the credential POST to a distinct /auth/login route (mirroring ScadaBridge, which
never collided because it posts to /auth/login). GET /login stays the Blazor page; the
cookie LoginPath stays /login. Adds a registration assertion pinning DashboardLoginPost
to /auth/login as the regression guard.
Files: Login.razor (LoginCard Action), DashboardEndpointRouteBuilderExtensions (MapPost
route), GatewayApplicationTests (route assertion).
34 lines
1.7 KiB
Plaintext
34 lines
1.7 KiB
Plaintext
@page "/login"
|
|
@layout LoginLayout
|
|
@using Microsoft.AspNetCore.Authorization
|
|
@* Login MUST stay anonymously reachable — [AllowAnonymous] overrides the
|
|
RequireAuthorization(ViewerPolicy) that MapRazorComponents<App>() applies, so the
|
|
cookie scheme's LoginPath="/login" redirect lands here for unauthenticated users.
|
|
|
|
The card is the shared kit's <LoginCard>: it renders a NATIVE static
|
|
<form method="post" action="/auth/login"> (username/password + hidden returnUrl). A native
|
|
form submit is not a Blazor event, so it reaches the minimal-API POST /auth/login endpoint
|
|
regardless of this app's InteractiveServer render mode. <AntiforgeryToken/> supplies the
|
|
token that PostLoginAsync's antiforgery.ValidateRequestAsync checks.
|
|
|
|
NOTE: the POST target is /auth/login, NOT /login. This @page lives at "/login" and the
|
|
Razor Components endpoint matches ALL methods, so a POST to /login collided with the
|
|
minimal-API MapPost("/login") and threw AmbiguousMatchException (HTTP 500). Posting to a
|
|
distinct /auth/login path (mirroring ScadaBridge) keeps the GET page and POST handler from
|
|
sharing a route. *@
|
|
@attribute [AllowAnonymous]
|
|
|
|
<LoginCard Product="MXAccess Gateway" Action="/auth/login" ReturnUrl="@ReturnUrl" Error="@Error">
|
|
<AntiforgeryToken />
|
|
</LoginCard>
|
|
|
|
@code {
|
|
/// <summary>Original protected URL the operator was bounced from; round-tripped to POST /login.</summary>
|
|
[SupplyParameterFromQuery(Name = "returnUrl")]
|
|
private string? ReturnUrl { get; set; }
|
|
|
|
/// <summary>Failure message surfaced by POST /login after a failed authentication.</summary>
|
|
[SupplyParameterFromQuery(Name = "error")]
|
|
private string? Error { get; set; }
|
|
}
|