/** * Login with Email Use Case * * Authenticates a user with email and password. */ import type { IUserRepository } from '../../domain/repositories/IUserRepository'; import type { AuthenticatedUserDTO } from '../dto/AuthenticatedUserDTO'; import type { AuthSessionDTO } from '../dto/AuthSessionDTO'; import type { IdentitySessionPort } from '../ports/IdentitySessionPort'; export interface LoginCommandDTO { email: string; password: string; } export class LoginWithEmailUseCase { constructor( private readonly userRepository: IUserRepository, private readonly sessionPort: IdentitySessionPort, ) {} async execute(command: LoginCommandDTO): Promise { // Validate inputs if (!command.email || !command.password) { throw new Error('Email and password are required'); } // Find user by email const user = await this.userRepository.findByEmail(command.email.toLowerCase().trim()); if (!user) { throw new Error('Invalid email or password'); } // Verify password const passwordHash = await this.hashPassword(command.password, user.salt); if (passwordHash !== user.passwordHash) { throw new Error('Invalid email or password'); } // Create session const authenticatedUser: AuthenticatedUserDTO = { id: user.id, displayName: user.displayName, email: user.email, primaryDriverId: user.primaryDriverId, }; return this.sessionPort.createSession(authenticatedUser); } private async hashPassword(password: string, salt: string): Promise { // Simple hash for demo - in production, use bcrypt or argon2 const data = password + salt; if (typeof crypto !== 'undefined' && crypto.subtle) { const encoder = new TextEncoder(); const dataBuffer = encoder.encode(data); const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } // Fallback for environments without crypto.subtle let hash = 0; for (let i = 0; i < data.length; i++) { const char = data.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; } return Math.abs(hash).toString(16).padStart(16, '0'); } }