using BCrypt.Net; using Knot.Contracts.Auth.Application.Abstractions; using Knot.Contracts.Auth.Application.Auth.DTOs; using Knot.Shared.Kernel; namespace Knot.Modules.Auth.Application.Users.Login; public sealed record LoginUserCommand(string Username, string Password) : ICommand; public sealed class LoginUserCommandHandler : ICommandHandler { private readonly IUserRepository _userRepository; private readonly IJwtTokenProvider _tokenProvider; public LoginUserCommandHandler(IUserRepository userRepository, IJwtTokenProvider tokenProvider) { _userRepository = userRepository; _tokenProvider = tokenProvider; } public async Task> Handle(LoginUserCommand request, CancellationToken cancellationToken) { var user = await _userRepository.GetByUsernameAsync(request.Username, cancellationToken); if (user is null || !BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash)) { return Result.Failure(AuthErrors.IdentityInvalidCredentials); } string token = _tokenProvider.Generate(user.Id, user.Username, user.DisplayName, user.Avatar); string refreshToken = _tokenProvider.GenerateRefreshToken(); DateTime refreshExpiry = _tokenProvider.GetRefreshTokenExpiry(); // Save refresh token to database user.SetRefreshToken(refreshToken, refreshExpiry); await _userRepository.UpdateAsync(user, cancellationToken); return Result.Success(new AuthResponseDto { AccessToken = token, RefreshToken = refreshToken, UserId = user.Id, Username = user.Username, DisplayName = user.DisplayName }); } }