A social knowledge tool for researchers built on ATProto
at development 112 lines 3.7 kB view raw
1import { UseCase } from 'src/shared/core/UseCase'; 2import { Result, err, ok } from 'src/shared/core/Result'; 3import { AppError } from 'src/shared/core/AppError'; 4import { IAppPasswordProcessor } from 'src/modules/atproto/application/IAppPasswordProcessor'; 5import { ITokenService } from '../services/ITokenService'; 6import { IUserRepository } from '../../domain/repositories/IUserRepository'; 7import { LoginWithAppPasswordDTO } from '../dtos/LoginWithAppPasswordDTO'; 8import { TokenPair } from '../dtos/TokenDTO'; 9import { DID } from '../../domain/value-objects/DID'; 10import { Handle } from '../../domain/value-objects/Handle'; 11import { LoginWithAppPasswordErrors } from './errors/LoginWithAppPasswordErrors'; 12import { IUserAuthenticationService } from '../../domain/services/IUserAuthenticationService'; 13 14export type LoginWithAppPasswordResponse = Result< 15 TokenPair, 16 | LoginWithAppPasswordErrors.InvalidCredentialsError 17 | LoginWithAppPasswordErrors.AuthenticationFailedError 18 | LoginWithAppPasswordErrors.TokenGenerationError 19 | AppError.UnexpectedError 20>; 21 22export class LoginWithAppPasswordUseCase 23 implements 24 UseCase<LoginWithAppPasswordDTO, Promise<LoginWithAppPasswordResponse>> 25{ 26 constructor( 27 private appPasswordProcessor: IAppPasswordProcessor, 28 private tokenService: ITokenService, 29 private userRepository: IUserRepository, 30 private userAuthService: IUserAuthenticationService, 31 ) {} 32 33 async execute( 34 request: LoginWithAppPasswordDTO, 35 ): Promise<LoginWithAppPasswordResponse> { 36 try { 37 // Validate input parameters 38 if (!request.identifier || !request.appPassword) { 39 return err(new LoginWithAppPasswordErrors.InvalidCredentialsError()); 40 } 41 42 // Process app password authentication 43 const authResult = await this.appPasswordProcessor.processAppPassword( 44 request.identifier, 45 request.appPassword, 46 ); 47 48 if (authResult.isErr()) { 49 return err( 50 new LoginWithAppPasswordErrors.AuthenticationFailedError( 51 authResult.error.message, 52 ), 53 ); 54 } 55 56 // Create DID value object 57 const didOrError = DID.create(authResult.value.did); 58 if (didOrError.isErr()) { 59 return err( 60 new LoginWithAppPasswordErrors.AuthenticationFailedError( 61 didOrError.error.message, 62 ), 63 ); 64 } 65 const did = didOrError.value; 66 67 // Create Handle value object if available 68 let handle: Handle | undefined; 69 if (authResult.value.handle) { 70 const handleOrError = Handle.create(authResult.value.handle); 71 if (handleOrError.isOk()) { 72 handle = handleOrError.value; 73 } 74 } 75 76 // Validate user credentials through domain service 77 const authenticationResult = 78 await this.userAuthService.validateUserCredentials(did, handle); 79 80 if (authenticationResult.isErr()) { 81 return err( 82 new LoginWithAppPasswordErrors.AuthenticationFailedError( 83 authenticationResult.error.message, 84 ), 85 ); 86 } 87 88 const user = authenticationResult.value.user; 89 90 // Record login 91 user.recordLogin(); 92 93 // Save updated user 94 await this.userRepository.save(user); 95 96 // Generate tokens 97 const tokenResult = await this.tokenService.generateToken(did.value); 98 99 if (tokenResult.isErr()) { 100 return err( 101 new LoginWithAppPasswordErrors.TokenGenerationError( 102 tokenResult.error.message, 103 ), 104 ); 105 } 106 107 return ok(tokenResult.value); 108 } catch (error: any) { 109 return err(new AppError.UnexpectedError(error)); 110 } 111 } 112}