using FoodsharingSiegen.Contracts; using FoodsharingSiegen.Contracts.Entity; using FoodsharingSiegen.Contracts.Enums; using FoodsharingSiegen.Contracts.Helper; using FoodsharingSiegen.Contracts.Model; using FoodsharingSiegen.Server.Data; using FoodsharingSiegen.Server.Service; using FoodsharingSiegen.Shared.Helper; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; namespace FoodsharingSiegen.Server.Auth { /// /// The auth service class (a. beging, 04.04.2022) /// public class AuthService { #region Public Properties /// /// Gets or sets the value of the user (ab) /// public User? User => _user; #endregion #region Private Properties /// /// Gets the value of the context (ab) /// private FsContext Context { get; } #endregion #region Private Fields /// /// The authentication state provider /// private readonly AuthenticationStateProvider _authenticationStateProvider; /// /// The local storage service /// private readonly LocalStorageService _localStorageService; /// /// The user /// private User? _user; /// /// The mail service /// private readonly IMailService _mailService; /// /// The application settings /// private readonly AppSettings _appSettings; #endregion #region Setup/Teardown /// /// Initializes a new instance of the class /// /// The context /// The local storage service /// The authentication state provider /// The mail service public AuthService( FsContext context, LocalStorageService localStorageService, AuthenticationStateProvider authenticationStateProvider, IMailService mailService, IOptions appSettings) { Context = context; _localStorageService = localStorageService; _authenticationStateProvider = authenticationStateProvider; _mailService = mailService; _appSettings = appSettings.Value; } #endregion #region Public Method Initialize /// /// Initializes this instance (a. beging, 11.04.2022) /// public async Task Initialize() { if (_user != null) return; var token = await _localStorageService.GetItem(StorageKeys.TokenKey); if (AuthHelper.ValidateToken(token, out var user) && user != null) _user = user; } #endregion #region Public Method Login /// /// Logins the mail address (a. beging, 04.04.2022) /// /// The mail address /// The password /// A task containing the operation result public async Task Login(string mailAddress, string password) { #region Ensure Admin var existingTroogS = await Context.Users!.AnyAsync(x => x.Mail == "fs@beging.de"); if (!existingTroogS) { var troogs = new User { Name = "Andre", Mail = "fs@beging.de", GroupsList = [UserGroup.Ambassador], Type = UserType.Admin, Created = DateTime.UtcNow, EncryptedPassword = "qSIxTZo7J8M=" }; await Context.Users!.AddAsync(troogs); await Context.SaveChangesAsync(); } #endregion Ensure Admin var encryptedPassword = Cryptor.Encrypt(password); _user = await Context.Users!.FirstOrDefaultAsync(x => x.Mail.ToLower() == mailAddress.ToLower() && x.EncryptedPassword == encryptedPassword); if (_user != null) { if (_user.Type == UserType.Unverified) { _user = null; return new OperationResult(new Exception("Anmeldung nicht möglich.")); } var serializedToken = AuthHelper.CreateToken(_user); await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken); if (_user.ForceLogout) { _user.ForceLogout = false; await Context.SaveChangesAsync(); } Context.Entry(_user).State = EntityState.Detached; return new OperationResult(); } return new OperationResult(new Exception("E-Mail-Adresse oder Passwort ist ungültig.")); } #endregion #region Public Method Logout /// /// Logouts this instance (a. beging, 04.04.2022) /// /// A task containing the operation result public async Task Logout() { try { await _localStorageService.RemoveItem(StorageKeys.TokenKey); _user = null; ((TokenAuthStateProvider) _authenticationStateProvider).MarkUserAsLoggedOut(); return new OperationResult(); } catch (Exception e) { return new OperationResult(e); } } #endregion #region Public Method RefreshState /// /// Refreshes the state (a. beging, 21.05.2022) /// public async Task RefreshState() { if (_user == null) return; _user = await Context.Users?.FirstOrDefaultAsync(x => x.Id == _user.Id)!; if (_user != null) { var serializedToken = AuthHelper.CreateToken(_user); await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken); } } #endregion #region Password Recovery public async Task InitiatePasswordReset(string email, string baseUri) { if (string.IsNullOrWhiteSpace(email)) return; var user = await Context.Users!.FirstOrDefaultAsync(x => x.Mail.ToLower() == email.ToLower()); if (user == null) return; // Do not leak existence var resetToken = Guid.NewGuid().ToString("N"); user.ResetToken = resetToken; user.ResetTokenExpiry = DateTime.UtcNow.AddMinutes(30); await Context.SaveChangesAsync(); var resetLink = $"{baseUri.TrimEnd('/')}/reset-password/{resetToken}"; var mailBody = $""" Hallo {user.Name},

für dein Konto wurde eine Anfrage zum Zurücksetzen des Passworts gestellt.
Wenn du diese Anfrage nicht gestellt hast, kannst du diese E-Mail ignorieren und dein Passwort bleibt unverändert.

Um dein Passwort zurückzusetzen, klicke bitte auf den folgenden Link (dieser ist 30 Minuten gültig):
{resetLink}

Viele Grüße
Dein Team {_appSettings.Terms.Title} """; await _mailService.SendEmailAsync(user.Mail, "Passwort zurücksetzen", mailBody); } public async Task ResetPassword(string token, string newPassword) { if (string.IsNullOrWhiteSpace(token)) return new OperationResult(new Exception("Ungültiges Token.")); if (string.IsNullOrWhiteSpace(newPassword)) return new OperationResult(new Exception("Passwort darf nicht leer sein.")); var user = await Context.Users!.FirstOrDefaultAsync(x => x.ResetToken == token && x.ResetTokenExpiry > DateTime.UtcNow); if (user == null) return new OperationResult(new Exception("Token ist ungültig oder abgelaufen.")); user.Password = newPassword; user.ResetToken = null; user.ResetTokenExpiry = null; await Context.SaveChangesAsync(); return new OperationResult(); } public async Task IsResetTokenValid(string token) { if (string.IsNullOrWhiteSpace(token)) return false; var user = await Context.Users!.FirstOrDefaultAsync(x => x.ResetToken == token && x.ResetTokenExpiry > DateTime.UtcNow); return user != null; } #endregion } }