using System.Security.Claims; using System.Net.Http.Json; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using timetracker.Shared; namespace timetracker.Client.Services; public class HostAuthenticationStateProvider : AuthenticationStateProvider { private readonly HttpClient _http; private readonly PersistentComponentState _state; private static readonly ClaimsPrincipal Anonymous = new(new ClaimsIdentity()); private ClaimsPrincipal? _currentUser; public HostAuthenticationStateProvider(HttpClient http, PersistentComponentState state) { _http = http; _state = state; } public override async Task GetAuthenticationStateAsync() { if (_currentUser != null) { return new AuthenticationState(_currentUser); } if (_state.TryTakeFromJson("UserInfoState", out var userInfo) && userInfo != null) { if (!userInfo.IsAuthenticated) { _currentUser = Anonymous; return new AuthenticationState(_currentUser); } var claims = new[] { new Claim(ClaimTypes.NameIdentifier, userInfo.Id.ToString()), new Claim(ClaimTypes.Name, userInfo.Username), new Claim("IsTenantAdmin", userInfo.IsTenantAdmin ? "true" : "false"), new Claim(ClaimTypes.Role, userInfo.IsTenantAdmin ? "TenantAdmin" : "Employee") }; var identity = new ClaimsIdentity(claims, "Cookie"); _currentUser = new ClaimsPrincipal(identity); return new AuthenticationState(_currentUser); } try { var response = await _http.GetAsync("api/auth/me"); if (response.IsSuccessStatusCode) { var apiUserInfo = await response.Content.ReadFromJsonAsync(); if (apiUserInfo != null) { var claims = new[] { new Claim(ClaimTypes.NameIdentifier, apiUserInfo.Id.ToString()), new Claim(ClaimTypes.Name, apiUserInfo.Username), new Claim("IsTenantAdmin", apiUserInfo.IsTenantAdmin ? "true" : "false"), new Claim(ClaimTypes.Role, apiUserInfo.IsTenantAdmin ? "TenantAdmin" : "Employee") }; var identity = new ClaimsIdentity(claims, "Cookie"); _currentUser = new ClaimsPrincipal(identity); return new AuthenticationState(_currentUser); } } } catch { // Ignore error and fall back to anonymous (e.g. server offline or network issues) } _currentUser = Anonymous; return new AuthenticationState(_currentUser); } public void NotifyUserChanged(UserInfo? userInfo) { if (userInfo != null) { var claims = new[] { new Claim(ClaimTypes.NameIdentifier, userInfo.Id.ToString()), new Claim(ClaimTypes.Name, userInfo.Username), new Claim("IsTenantAdmin", userInfo.IsTenantAdmin ? "true" : "false"), new Claim(ClaimTypes.Role, userInfo.IsTenantAdmin ? "TenantAdmin" : "Employee") }; var identity = new ClaimsIdentity(claims, "Cookie"); _currentUser = new ClaimsPrincipal(identity); } else { _currentUser = Anonymous; } NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_currentUser))); } }