WBS-11 BFF Hardening: Implement auth caching state provider for instant sidebar transitions, forward proxy authentication cookies, and resolve WASM server-side prerendering BaseAddress null exception
WBS-9.3 - NULL Policy CI Gate / NULL Policy Validation (push) Failing after 8s
Quant Engine CI/CD Pipeline / validate-core (push) Failing after 14s
Quant Engine CI/CD Pipeline / validate-ui-and-storage (push) Has been skipped
Deploy to Production / Build & Deploy to Production (push) Failing after 3m7s

This commit is contained in:
2026-07-06 16:40:56 +09:00
parent f35d694df4
commit ed21b0874e
10 changed files with 80 additions and 384 deletions
+20 -10
View File
@@ -93,7 +93,11 @@ builder.Services.AddScoped<IKisApiClient, KisApiClient>();
// builder.Services.AddScoped<ICollectionOrchestrator, KisDataCollectionOrchestrator>();
// builder.Services.AddScoped<DataCollectionService>();
builder.Services.AddHttpClient<ApiClient>();
builder.Services.AddHttpClient<ApiClient>(client =>
{
// Configure default base address for relative HttpClient calls within server assembly calls
client.BaseAddress = new Uri("http://localhost:5265/");
});
builder.Services.AddScoped<ApiClient>();
builder.Services.AddFastEndpoints();
@@ -183,13 +187,15 @@ catch (Exception ex)
Log.Warning("Hangfire setup failed: {Message}", ex.Message);
}
// Root path - redirect unauthenticated to /login.html (static file)
// Root path - redirect unauthenticated to /Account/Login (secure SSR Razor Page)
app.MapGet("/", async (HttpContext ctx) =>
{
var isAuthenticated = ctx.User?.Identity?.IsAuthenticated ?? false;
if (!isAuthenticated)
// Check cookie parity for server-side root routing redirect
var hasCookie = ctx.Request.Cookies.ContainsKey("quant_auth_token");
if (!isAuthenticated && !hasCookie)
{
ctx.Response.Redirect("/login.html");
ctx.Response.Redirect("/Account/Login");
}
else
{
@@ -199,10 +205,10 @@ app.MapGet("/", async (HttpContext ctx) =>
await Task.CompletedTask;
});
// Map /login to static login.html
// Map /login to secure SSR Razor Page /Account/Login
app.MapGet("/login", (HttpContext ctx) =>
{
ctx.Response.Redirect("/login.html", permanent: false);
ctx.Response.Redirect("/Account/Login", permanent: false);
});
// Login API (API-First for Blazor WASM client authentication)
@@ -243,6 +249,8 @@ app.MapPost("/api/auth/login", async (JsonElement payload, HttpContext httpConte
var devToken = Guid.NewGuid().ToString("N");
var devExpiresAt = DateTimeOffset.UtcNow.AddDays(7);
var devIsSecureEnv = httpContext.Request.IsHttps;
// Set HTTP-only cookie for dev fallback too
httpContext.Response.Cookies.Append(
"quant_auth_token",
@@ -250,7 +258,7 @@ app.MapPost("/api/auth/login", async (JsonElement payload, HttpContext httpConte
new Microsoft.AspNetCore.Http.CookieOptions
{
HttpOnly = true,
Secure = true,
Secure = devIsSecureEnv,
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax,
Expires = devExpiresAt,
Path = "/"
@@ -299,13 +307,15 @@ app.MapPost("/api/auth/login", async (JsonElement payload, HttpContext httpConte
Console.WriteLine($"[Auth/Login] Setting cookie 'quant_auth_token'");
Console.WriteLine($"[Auth/Login] IsHttps: {httpContext.Request.IsHttps}");
var isSecureEnv = httpContext.Request.IsHttps;
httpContext.Response.Cookies.Append(
"quant_auth_token",
rawToken,
new Microsoft.AspNetCore.Http.CookieOptions
{
HttpOnly = true,
Secure = true, // Force Secure Cookie for SSL standard
Secure = isSecureEnv, // Dynamic SSL Secure binding based on active request env
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax,
Expires = expiresAt,
Path = "/"
@@ -539,8 +549,8 @@ internal sealed class QuantAdminAuthHandler : AuthenticationHandler<Authenticati
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
// Instead of hard 401 response which breaks routing or triggers static page redirects, redirect to login page
Response.Redirect("/login.html");
// Redirect securely to Razor Page Login endpoint
Response.Redirect("/Account/Login");
return Task.CompletedTask;
}
}