/** * Login with Email Use Case * * Authenticates a user with email and password. */ import type { UserRepository } from '../../domain/repositories/UserRepository'; import type { IdentitySessionPort } from '../ports/IdentitySessionPort'; import { Result } from '@core/shared/domain/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import type { Logger } from '@core/shared/application'; import { PasswordHash } from '@core/identity/domain/value-objects/PasswordHash'; export type LoginWithEmailInput = { email: string; password: string; }; export type LoginWithEmailResult = { sessionToken: string; userId: string; displayName: string; email?: string; primaryDriverId?: string; issuedAt: number; expiresAt: number; }; export type LoginWithEmailErrorCode = | 'INVALID_INPUT' | 'INVALID_CREDENTIALS' | 'REPOSITORY_ERROR'; export type LoginWithEmailApplicationError = ApplicationErrorCode< LoginWithEmailErrorCode, { message: string } >; export class LoginWithEmailUseCase { constructor( private readonly userRepository: IUserRepository, private readonly sessionPort: IdentitySessionPort, private readonly logger: Logger, ) {} async execute(input: LoginWithEmailInput): Promise> { try { if (!input.email || !input.password) { return Result.err({ code: 'INVALID_INPUT', details: { message: 'Email and password are required' }, } as LoginWithEmailApplicationError); } const normalizedEmail = input.email.toLowerCase().trim(); const user = await this.userRepository.findByEmail(normalizedEmail); if (!user) { return Result.err({ code: 'INVALID_CREDENTIALS', details: { message: 'Invalid email or password' }, } as LoginWithEmailApplicationError); } // Verify password using PasswordHash value object const storedPasswordHash = PasswordHash.fromHash(user.passwordHash); const isValid = await storedPasswordHash.verify(input.password); if (!isValid) { return Result.err({ code: 'INVALID_CREDENTIALS', details: { message: 'Invalid email or password' }, } as LoginWithEmailApplicationError); } type CreateSessionInput = Parameters[0]; const createSessionInput = { id: user.id, displayName: user.displayName, ...(user.email !== undefined ? { email: user.email } : {}), ...(user.primaryDriverId !== undefined ? { primaryDriverId: user.primaryDriverId } : {}), } satisfies CreateSessionInput; const session = await this.sessionPort.createSession(createSessionInput); const result: LoginWithEmailResult = { sessionToken: session.token, userId: session.user.id, displayName: session.user.displayName, ...(session.user.email !== undefined ? { email: session.user.email } : {}), ...(session.user.primaryDriverId !== undefined ? { primaryDriverId: session.user.primaryDriverId } : {}), issuedAt: session.issuedAt, expiresAt: session.expiresAt, }; return Result.ok(result); } catch (error) { const message = error instanceof Error && error.message ? error.message : 'Failed to execute LoginWithEmailUseCase'; this.logger.error( 'LoginWithEmailUseCase.execute failed', error instanceof Error ? error : undefined, { input }, ); return Result.err({ code: 'REPOSITORY_ERROR', details: { message }, } as LoginWithEmailApplicationError); } } }