using AuthorBuddy.Web.Data; using LiteDB; namespace AuthorBuddy.Web.Services; public class AuthService : IAuthService { private readonly ILiteDatabase _db; private readonly ISettingsService _settingsService; private UserEntity? _currentUser; private string? _pending2FASecret; private bool _requires2FA; public AuthService(ILiteDatabase db, ISettingsService settingsService) { _db = db; _settingsService = settingsService; } public bool IsLoggedIn => _currentUser != null; public bool Is2FARequired => _requires2FA; public async Task LoginAsync(string username, string password) { var col = _db.GetCollection("users"); var user = col.FindOne(u => u.Name == username); if (user == null || string.IsNullOrEmpty(user.PasswordHash)) return false; if (!TotpHelper.VerifyPassword(password, user.PasswordHash)) return false; if (user.TwoFactorEnabled) { _pending2FASecret = user.TwoFactorSecret; _requires2FA = true; return true; } await SetAuthenticatedUser(user); return true; } public async Task LogoutAsync() { _currentUser = null; _requires2FA = false; _pending2FASecret = null; await Task.CompletedTask; } public async Task AdminBypassLoginAsync(int userId) { var col = _db.GetCollection("users"); var user = col.FindById(userId); if (user != null) await SetAuthenticatedUser(user); } public async Task RegisterAsync(string username, string password) { var col = _db.GetCollection("users"); var existing = col.FindOne(u => u.Name == username); if (existing != null) return false; var user = new UserEntity { Name = username, PasswordHash = TotpHelper.HashPassword(password), IsCurrent = true, TwoFactorEnabled = false, TwoFactorSecret = string.Empty }; col.Insert(user); await SetAuthenticatedUser(user); return true; } public Task GetCurrentUserAsync() { return Task.FromResult(_currentUser); } public async Task Verify2FAAsync(string code) { if (string.IsNullOrEmpty(_pending2FASecret)) return false; if (!TotpHelper.VerifyCode(_pending2FASecret, code)) return false; var col = _db.GetCollection("users"); var user = col.FindOne(u => u.TwoFactorSecret == _pending2FASecret); if (user == null) return false; await SetAuthenticatedUser(user); return true; } public Task Generate2FASecretAsync() { return Task.FromResult(TotpHelper.GenerateSecret()); } public string Get2FAProvisioningUri(string secret, string username) { return TotpHelper.GetProvisioningUri(secret, username); } public async Task Enable2FAAsync(string secret) { if (_currentUser == null) return false; var col = _db.GetCollection("users"); var user = col.FindById(_currentUser.Id); if (user == null) return false; user.TwoFactorSecret = secret; user.TwoFactorEnabled = true; col.Update(user); _currentUser.TwoFactorSecret = secret; _currentUser.TwoFactorEnabled = true; return true; } public async Task Disable2FAAsync() { if (_currentUser == null) return false; var col = _db.GetCollection("users"); var user = col.FindById(_currentUser.Id); if (user == null) return false; user.TwoFactorSecret = string.Empty; user.TwoFactorEnabled = false; col.Update(user); _currentUser.TwoFactorSecret = string.Empty; _currentUser.TwoFactorEnabled = false; return true; } public async Task Is2FAEnabledAsync() { if (_currentUser == null) return false; var col = _db.GetCollection("users"); var user = col.FindById(_currentUser.Id); return user?.TwoFactorEnabled ?? false; } public async Task GetCurrent2FASecretAsync() { if (_currentUser == null) return null; var col = _db.GetCollection("users"); var user = col.FindById(_currentUser.Id); return user?.TwoFactorSecret; } private async Task SetAuthenticatedUser(UserEntity user) { _currentUser = user; _requires2FA = false; _pending2FASecret = null; _settingsService.CurrentProjectId = null; _settingsService.CurrentFilePath = null; var col = _db.GetCollection("users"); var all = col.FindAll().ToList(); foreach (var u in all) { u.IsCurrent = u.Id == user.Id; col.Update(u); } await _settingsService.InitializeAsync(); } }