Backend login and register
This commit is contained in:
147
src/Application/Services/AuthenticationService.cs
Normal file
147
src/Application/Services/AuthenticationService.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user