using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; using Microsoft.EntityFrameworkCore; using radiuscontroller.Data; using radiuscontroller.Models; namespace radiuscontroller.Services; public class AdminAuthService { private readonly RadiusDbContext _db; private readonly ProtectedSessionStorage _sessionStorage; private const string SessionKey = "AdminUsername"; public string? CurrentAdminUsername { get; private set; } public bool IsAuthenticated => !string.IsNullOrEmpty(CurrentAdminUsername); public event Action? OnAuthStateChanged; public AdminAuthService(RadiusDbContext db, ProtectedSessionStorage sessionStorage) { _db = db; _sessionStorage = sessionStorage; } public async Task InitializeAsync() { try { var result = await _sessionStorage.GetAsync(SessionKey); if (result.Success && !string.IsNullOrEmpty(result.Value)) { CurrentAdminUsername = result.Value; NotifyStateChanged(); } } catch { // Session storage might fail during prerendering or early socket connection CurrentAdminUsername = null; } } public async Task HasAnyAdminAsync() { return await _db.AdminUsers.AnyAsync(); } public async Task RegisterInitialAdminAsync(string username, string password) { if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) return false; // Ensure first-time setup is only allowed if no admin users exist if (await _db.AdminUsers.AnyAsync()) return false; string cleanUsername = username.Trim().ToLowerInvariant(); string hash = BCrypt.Net.BCrypt.HashPassword(password.Trim()); var admin = new AdminUser { Username = cleanUsername, PasswordHash = hash, CreatedAt = DateTime.UtcNow }; _db.AdminUsers.Add(admin); await _db.SaveChangesAsync(); CurrentAdminUsername = admin.Username; await _sessionStorage.SetAsync(SessionKey, admin.Username); NotifyStateChanged(); return true; } public async Task LoginAsync(string username, string password) { if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) return false; var admin = await _db.AdminUsers.FirstOrDefaultAsync(u => u.Username == username.Trim().ToLowerInvariant()); if (admin == null) return false; bool isValid = BCrypt.Net.BCrypt.Verify(password, admin.PasswordHash); if (isValid) { CurrentAdminUsername = admin.Username; await _sessionStorage.SetAsync(SessionKey, admin.Username); NotifyStateChanged(); return true; } return false; } public async Task LogoutAsync() { CurrentAdminUsername = null; try { await _sessionStorage.DeleteAsync(SessionKey); } catch { // Ignore storage errors during logout } NotifyStateChanged(); } private void NotifyStateChanged() => OnAuthStateChanged?.Invoke(); }