Backend login and register

This commit is contained in:
2026-02-19 13:49:01 +01:00
parent 0b6bb019b6
commit 93a78e4614
62 changed files with 11588 additions and 13 deletions

View File

@@ -0,0 +1,147 @@
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<Result> 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<User>();
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<Result> 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<User>();
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<Result> 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<Result> 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<Result> 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<User>();
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");
}
}