using Domain.Exceptions;
using Features.Auth.Dtos;
using Features.Auth.Repository;
using Features.Auth.Services;
using Infrastructure.PasswordHashing;
using MediatR;
namespace Features.Auth.Queries.Login;
///
/// Handles by verifying credentials and issuing access/refresh tokens.
///
/// Repository used to look up the user account and its active credential.
/// Infrastructure component used to verify a plain-text password against a stored hash.
/// Service used to generate access and refresh tokens for the authenticated user.
public class LoginHandler(
IAuthRepository authRepo,
IPasswordInfrastructure passwordInfrastructure,
ITokenService tokenService
) : IRequestHandler
{
///
/// Thrown when the username does not match any account, the account has no active credential,
/// or the supplied password does not match the stored hash.
///
public async Task Handle(LoginQuery request, CancellationToken cancellationToken)
{
var user =
await authRepo.GetUserByUsernameAsync(request.Username)
?? throw new UnauthorizedException("Invalid username or password.");
// @todo handle expired passwords
var activeCred =
await authRepo.GetActiveCredentialByUserAccountIdAsync(user.UserAccountId)
?? throw new UnauthorizedException("Invalid username or password.");
if (!passwordInfrastructure.Verify(request.Password, activeCred.Hash))
throw new UnauthorizedException("Invalid username or password.");
var accessToken = tokenService.GenerateAccessToken(user);
var refreshToken = tokenService.GenerateRefreshToken(user);
return new LoginPayload(user.UserAccountId, user.Username, refreshToken, accessToken);
}
}