password.ts (1206B)
1 import { 2 PASSWORD_ATTEMPT_LIMIT, 3 PASSWORD_ATTEMPT_TTL, 4 } from "#core/domain/constants.ts"; 5 import { PasswordHash } from "#core/domain/crypto.ts"; 6 import { DomainError } from "#core/domain/error.ts"; 7 import { Limiter } from "#core/domain/limiter.ts"; 8 9 export class PasswordMismatch extends DomainError { 10 constructor(readonly delay: number) { 11 super("Password mismatched"); 12 } 13 } 14 15 export class Password { 16 private _attemptLimit: Limiter; 17 18 static hash(candidate: string): Password { 19 return new Password(PasswordHash.hash(candidate)); 20 } 21 22 constructor( 23 readonly hash: PasswordHash, 24 attemptCount?: number, 25 attemptExpire?: number, 26 ) { 27 this._attemptLimit = new Limiter( 28 PASSWORD_ATTEMPT_LIMIT, 29 PASSWORD_ATTEMPT_TTL, 30 attemptCount, 31 attemptExpire, 32 ); 33 } 34 35 get attemptCount() { 36 return this._attemptLimit.count; 37 } 38 39 get attemptExpire() { 40 return this._attemptLimit.expire; 41 } 42 43 attempt(candidate: string): void { 44 this._attemptLimit.increment(); 45 if (!this.verify(candidate)) { 46 throw new PasswordMismatch(this._attemptLimit.delay); 47 } 48 } 49 50 verify(candidate: string): boolean { 51 return this.hash.verify(candidate); 52 } 53 }