feat(web): add auth and fix deployment checks
Quant Engine CI/CD Pipeline / validate-core (push) Failing after 9s
WBS-9.3 - NULL Policy CI Gate / NULL Policy Validation (push) Failing after 6s
Quant Engine CI/CD Pipeline / validate-ui-and-storage (push) Has been skipped
Snapshot Admin Deployment / build-and-deploy (push) Failing after 2m30s
Deploy to Production / Build & Deploy to Production (push) Failing after 3m49s

This commit is contained in:
2026-07-01 13:02:10 +09:00
parent 3e4d545e01
commit 90bbb1860d
17 changed files with 445 additions and 53 deletions
@@ -7,25 +7,41 @@ namespace QuantEngine.Web.Client.Infrastructure
public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly LocalStorageService _localStorage;
private readonly HttpClient _http;
private readonly ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity());
private const string StorageKey = "quant_admin_session";
private const string TokenKey = "quant_admin_access_token";
private const string UsernameKey = "quant_admin_username";
private const string RoleKey = "quant_admin_role";
public CustomAuthenticationStateProvider(LocalStorageService localStorage)
public CustomAuthenticationStateProvider(LocalStorageService localStorage, HttpClient http)
{
_localStorage = localStorage;
_http = http;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
try
{
var username = await _localStorage.GetAsync<string>(StorageKey);
if (!string.IsNullOrEmpty(username))
var token = await _localStorage.GetAsync<string>(TokenKey);
var username = await _localStorage.GetAsync<string>(UsernameKey);
var role = await _localStorage.GetAsync<string>(RoleKey) ?? "Admin";
if (!string.IsNullOrWhiteSpace(token) && !string.IsNullOrWhiteSpace(username))
{
var request = new HttpRequestMessage(HttpMethod.Get, "api/auth/me");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var response = await _http.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
await MarkUserAsLoggedOutAsync();
return new AuthenticationState(_anonymous);
}
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, "Admin")
new Claim(ClaimTypes.Role, role)
}, "QuantAdminAuth");
var user = new ClaimsPrincipal(identity);
@@ -40,14 +56,16 @@ namespace QuantEngine.Web.Client.Infrastructure
return new AuthenticationState(_anonymous);
}
public async Task MarkUserAsAuthenticatedAsync(string username)
public async Task MarkUserAsAuthenticatedAsync(string username, string accessToken, string role)
{
await _localStorage.SetAsync(StorageKey, username);
await _localStorage.SetAsync(TokenKey, accessToken);
await _localStorage.SetAsync(UsernameKey, username);
await _localStorage.SetAsync(RoleKey, role);
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, "Admin")
new Claim(ClaimTypes.Role, role)
}, "QuantAdminAuth");
var user = new ClaimsPrincipal(identity);
@@ -56,8 +74,30 @@ namespace QuantEngine.Web.Client.Infrastructure
public async Task MarkUserAsLoggedOutAsync()
{
await _localStorage.DeleteAsync(StorageKey);
await _localStorage.DeleteAsync(TokenKey);
await _localStorage.DeleteAsync(UsernameKey);
await _localStorage.DeleteAsync(RoleKey);
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_anonymous)));
}
public async Task LogoutFromServerAsync()
{
var token = await _localStorage.GetAsync<string>(TokenKey);
if (!string.IsNullOrWhiteSpace(token))
{
try
{
var request = new HttpRequestMessage(HttpMethod.Post, "api/auth/logout");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
await _http.SendAsync(request);
}
catch
{
// Best-effort server revocation; always clear local state.
}
}
await MarkUserAsLoggedOutAsync();
}
}
}