email_challenge.ts (1654B)
1 import { AuthRepository } from "#core/application/authn/auth_repository.ts"; 2 import { EntityNotFound } from "../repository_error.ts"; 3 import { AlreadyVerifiedCodeChallenge } from "#core/domain/code_challenge.ts"; 4 import { ExceedingLimit } from "#core/domain/limiter.ts"; 5 import { InvalidUUID, UUID } from "#core/domain/uuid.ts"; 6 7 export type AuthEmailChallengeRequest = { 8 uuid: string; 9 }; 10 11 export type AuthEmailChallengeResponse = { 12 status: "sent" | "invalid" | "verified", 13 delay: number 14 } 15 16 export interface AuthEmailChallengeMailer { 17 send(email: string, code: string): Promise<void> | void; 18 } 19 20 export class AuthEmailChallengeUseCase { 21 constructor( 22 private readonly repo: AuthRepository, 23 private readonly mailer: AuthEmailChallengeMailer, 24 ) { 25 } 26 27 async execute( 28 request: AuthEmailChallengeRequest, 29 ): Promise<AuthEmailChallengeResponse> { 30 try { 31 const uuid = new UUID(request.uuid); 32 const auth = await this.repo.find(uuid); 33 const code = auth.requestEmailChallenge(); 34 await this.repo.store(auth); 35 await this.mailer.send( 36 auth.email.address.toString(), 37 code.toString(), 38 ); 39 return { status: "sent", delay: auth.email.requestDelay }; 40 } catch (error) { 41 if (error instanceof ExceedingLimit) { 42 return { status: "sent", delay: error.delay }; 43 } 44 if ( 45 error instanceof InvalidUUID || 46 error instanceof EntityNotFound 47 ) { 48 return { status: "invalid", delay: 0 }; 49 } 50 if (error instanceof AlreadyVerifiedCodeChallenge) { 51 return { status: "verified", delay: 0 }; 52 } 53 throw error; 54 } 55 } 56 }