pos-be/src/core/helpers/otp/otp-service.ts

67 lines
1.7 KiB
TypeScript

interface OtpServiceEntity {
length?: number;
}
export class OtpService {
private readonly otpLength: number;
constructor({ length = 4 }: OtpServiceEntity) {
this.otpLength = Math.max(length, 4); // Minimum of 4 digits
}
private hasSequentialDigits(str: string): boolean {
for (let i = 0; i < str.length - 1; i++) {
const current = parseInt(str[i], 10);
const next = parseInt(str[i + 1], 10);
if (next === current + 1 || next === current - 1) {
return true;
}
}
return false;
}
private hasRepeatedDigits(str: string): boolean {
return str.split('').every((char) => char === str[0]);
}
private isPalindrome(str: string): boolean {
return str === str.split('').reverse().join('');
}
private hasPartiallyRepeatedDigits(str: string): boolean {
const counts: Record<string, number> = {};
for (const char of str) {
counts[char] = (counts[char] || 0) + 1;
}
// Reject if any digit appears more than twice
return Object.values(counts).some((count) => count > 2);
}
private hasNoMatchLength(str: string) {
return str.length !== this.otpLength;
}
private hasStartWithZero(str: string) {
return str.split('')[0] === '0';
}
public generateSecureOTP(): string {
let otp: string;
do {
otp = Array.from({ length: this.otpLength }, () =>
Math.floor(Math.random() * 10).toString(),
).join('');
} while (
this.hasNoMatchLength(otp) ||
this.hasSequentialDigits(otp) ||
this.hasRepeatedDigits(otp) ||
this.isPalindrome(otp) ||
this.hasPartiallyRepeatedDigits(otp) ||
this.hasStartWithZero(otp)
);
return otp;
}
}