71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
/**
|
|
* 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<AuthSessionDTO> {
|
|
// 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<string> {
|
|
// 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');
|
|
}
|
|
} |