using ColaFlow.Modules.Identity.Application.Dtos; using ColaFlow.Modules.Identity.Application.Services; using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants; using ColaFlow.Modules.Identity.Domain.Aggregates.Users; using ColaFlow.Modules.Identity.Domain.Repositories; using MediatR; namespace ColaFlow.Modules.Identity.Application.Commands.Login; public class LoginCommandHandler : IRequestHandler { private readonly ITenantRepository _tenantRepository; private readonly IUserRepository _userRepository; private readonly IJwtService _jwtService; private readonly IPasswordHasher _passwordHasher; private readonly IRefreshTokenService _refreshTokenService; private readonly IUserTenantRoleRepository _userTenantRoleRepository; public LoginCommandHandler( ITenantRepository tenantRepository, IUserRepository userRepository, IJwtService jwtService, IPasswordHasher passwordHasher, IRefreshTokenService refreshTokenService, IUserTenantRoleRepository userTenantRoleRepository) { _tenantRepository = tenantRepository; _userRepository = userRepository; _jwtService = jwtService; _passwordHasher = passwordHasher; _refreshTokenService = refreshTokenService; _userTenantRoleRepository = userTenantRoleRepository; } public async Task Handle(LoginCommand request, CancellationToken cancellationToken) { // 1. Find tenant var slug = TenantSlug.Create(request.TenantSlug); var tenant = await _tenantRepository.GetBySlugAsync(slug, cancellationToken); if (tenant == null) { throw new UnauthorizedAccessException("Invalid credentials"); } // 2. Find user var email = Email.Create(request.Email); var user = await _userRepository.GetByEmailAsync(TenantId.Create(tenant.Id), email, cancellationToken); if (user == null) { throw new UnauthorizedAccessException("Invalid credentials"); } // 3. Verify password if (user.PasswordHash == null || !_passwordHasher.VerifyPassword(request.Password, user.PasswordHash)) { throw new UnauthorizedAccessException("Invalid credentials"); } // 4. Get user's tenant role var userTenantRole = await _userTenantRoleRepository.GetByUserAndTenantAsync( user.Id, tenant.Id, cancellationToken); if (userTenantRole == null) { throw new InvalidOperationException($"User {user.Id} has no role assigned for tenant {tenant.Id}"); } // 5. Generate JWT token with role var accessToken = _jwtService.GenerateToken(user, tenant, userTenantRole.Role); // 6. Generate refresh token var refreshToken = await _refreshTokenService.GenerateRefreshTokenAsync( user, ipAddress: null, userAgent: null, cancellationToken); // 7. Update last login time user.RecordLogin(); await _userRepository.UpdateAsync(user, cancellationToken); // 8. Return result return new LoginResponseDto { User = new UserDto { Id = user.Id, TenantId = tenant.Id, Email = user.Email.Value, FullName = user.FullName.Value, Status = user.Status.ToString(), AuthProvider = user.AuthProvider.ToString(), IsEmailVerified = user.EmailVerifiedAt.HasValue, LastLoginAt = user.LastLoginAt, CreatedAt = user.CreatedAt }, Tenant = new TenantDto { Id = tenant.Id, Name = tenant.Name.Value, Slug = tenant.Slug.Value, Status = tenant.Status.ToString(), Plan = tenant.Plan.ToString(), SsoEnabled = tenant.SsoConfig != null, SsoProvider = tenant.SsoConfig?.Provider.ToString(), CreatedAt = tenant.CreatedAt, UpdatedAt = tenant.UpdatedAt ?? tenant.CreatedAt }, AccessToken = accessToken, RefreshToken = refreshToken }; } }