using System.Security.Cryptography; using Application.Common.Results; using Application.DTOs; using Application.Errors; using Application.Interfaces; using Application.Models; using Application.Validators; using Domain.Entities; using Domain.Interface; using Infrastructure.Utilities; using Microsoft.AspNetCore.Identity; namespace Application.Services; public class AuthenticationService( IUnitOfWork unitOfWork, IUserRepository iUserRepository, LoginRequestValidator loginRequestValidator, RegisterRequestValidator registerRequestValidator, IJwtService jwtService, IEmailService emailService) : IAuthenticationService { public async Task RegisterAsync(RegisterRequest registerRequest) { var validationResult = await registerRequestValidator.ValidateAsync(registerRequest); if (!validationResult.IsValid) { var errors = validationResult.Errors.Select(x => x.ErrorMessage); return Result.Failure(AuthError.CreateInvalidRegisterRequestError(errors)); } var emailExists = await iUserRepository.GetUserByEmailAsync(registerRequest.Email); if (emailExists is not null) return Result.Failure(AuthError.EmailAlreadyExists); var usernameExists = await iUserRepository.GetUserByUsernameAsync(registerRequest.Username); if (usernameExists is not null) return Result.Failure(AuthError.UsernameAlreadyExists); var user = new User { Username = registerRequest.Username, Email = registerRequest.Email, Password = registerRequest.Password, UserRoles = [new UserRole { RoleId = 3 }] }; var passwordHasher = new PasswordHasher(); var hashedPassword = passwordHasher.HashPassword(user, registerRequest.Password); user.Password = hashedPassword; await iUserRepository.AddAsync(user); await unitOfWork.CommitAsync(); return Result.Success("User registered successfully."); } public async Task LoginAsync(LoginRequest loginRequest) { var validationResult = await loginRequestValidator.ValidateAsync(loginRequest); if (!validationResult.IsValid) { var errors = validationResult.Errors.Select(x => x.ErrorMessage); return Result.Failure(AuthError.CreateInvalidLoginRequestError(errors)); } var (email, password) = loginRequest; var user = await iUserRepository.GetUserByEmailAsync(email); if (user is null) return Result.Failure(AuthError.UserNotFound); var passwordHasher = new PasswordHasher(); var verificationResult = passwordHasher.VerifyHashedPassword(user, user.Password, password); if (verificationResult == PasswordVerificationResult.Failed) return Result.Failure(AuthError.InvalidPassword); user.LastLogin = DateTime.UtcNow; iUserRepository.Update(user); var token = new TokenResponse { AccessToken = await jwtService.GenerateTokenAsync(user), RefreshToken = await jwtService.GenerateAndSaveRefreshTokenAsync(user) }; var result = new { Token = token, user.Username }; return Result.Success(result); } public async Task RefreshTokensAsync(RefreshTokenRequest request) { var user = await jwtService.ValidateRefreshTokenAsync(request.UserId, request.RefreshToken); if (user is null) return Result.Failure(AuthError.UserNotFound); var result = new TokenResponse { AccessToken = await jwtService.GenerateTokenAsync(user), RefreshToken = await jwtService.GenerateAndSaveRefreshTokenAsync(user) }; return Result.Success(result); } public async Task SendResetEmailAsync(string email) { var user = await iUserRepository.GetUserByEmailAsync(email); if (user is null) return Result.Failure(AuthError.UserNotFound); var tokenBytes = RandomNumberGenerator.GetBytes(64); var emailToken = Convert.ToBase64String(tokenBytes); user.ResetPasswordToken = emailToken; user.ResetPasswordTokenExpiryTime = DateTime.UtcNow.AddMinutes(15); var emailModel = new EmailRequest(email, "Reset Password!!", EmailBody.EmailStringBody(email, emailToken)); emailService.SendEmailAsync(emailModel); iUserRepository.Update(user); await unitOfWork.CommitAsync(); return Result.Success("Reset email sent successfully"); } public async Task ResetPasswordAsync(ResetPasswordDto resetPasswordDto) { // Normalize the incoming token if '+' was converted to space by transport layers. var normalizedToken = (resetPasswordDto.EmailToken ?? string.Empty).Replace(" ", "+"); var user = await iUserRepository.GetUserByEmailAsync(resetPasswordDto.Email); if (user is null) return Result.Failure(AuthError.UserNotFound); var tokenCode = user.ResetPasswordToken; var emailTokenExpiryTime = user.ResetPasswordTokenExpiryTime; // Validate token and expiration using UTC to match stored times if (string.IsNullOrWhiteSpace(normalizedToken) || tokenCode != normalizedToken || emailTokenExpiryTime < DateTime.UtcNow) return Result.Failure(AuthError.InvalidResetLink); var passwordHasher = new PasswordHasher(); var hashedPassword = passwordHasher.HashPassword(user, resetPasswordDto.NewPassword); user.Password = hashedPassword; // Invalidate the reset token after successful use user.ResetPasswordToken = null; user.ResetPasswordTokenExpiryTime = default; iUserRepository.Update(user); await unitOfWork.CommitAsync(); return Result.Success("Password reset successfully"); } }