81 lines
1.6 KiB
TypeScript
81 lines
1.6 KiB
TypeScript
export interface PasswordStrength {
|
|
score: number; // 0-4
|
|
label: string;
|
|
color: string; // Tailwind color class prefix (e.g., 'red', 'yellow', 'green')
|
|
feedback: string[];
|
|
}
|
|
|
|
export function calculatePasswordStrength(password: string): PasswordStrength {
|
|
let score = 0;
|
|
const feedback: string[] = [];
|
|
|
|
if (!password) {
|
|
return {
|
|
score: 0,
|
|
label: 'Empty',
|
|
color: 'gray',
|
|
feedback: [],
|
|
};
|
|
}
|
|
|
|
// Length check
|
|
if (password.length < 8) {
|
|
feedback.push('Too short (min 8 chars)');
|
|
} else {
|
|
score += 1;
|
|
}
|
|
|
|
if (password.length >= 12) {
|
|
score += 1;
|
|
}
|
|
|
|
// Complexity checks
|
|
const hasLower = /[a-z]/.test(password);
|
|
const hasUpper = /[A-Z]/.test(password);
|
|
const hasNumber = /\d/.test(password);
|
|
const hasSpecial = /[^A-Za-z0-9]/.test(password);
|
|
|
|
const varietyCount = [hasLower, hasUpper, hasNumber, hasSpecial].filter(Boolean).length;
|
|
|
|
if (varietyCount >= 3) {
|
|
score += 1;
|
|
}
|
|
if (varietyCount === 4) {
|
|
score += 1;
|
|
}
|
|
|
|
// Penalties
|
|
if (varietyCount < 2 && password.length >= 8) {
|
|
feedback.push('Add more variety (uppercase, numbers, symbols)');
|
|
}
|
|
|
|
// Cap score at 4
|
|
score = Math.min(score, 4);
|
|
|
|
// Determine label and color
|
|
let label = 'Very Weak';
|
|
let color = 'red';
|
|
|
|
switch (score) {
|
|
case 0:
|
|
case 1:
|
|
label = 'Weak';
|
|
color = 'red';
|
|
break;
|
|
case 2:
|
|
label = 'Fair';
|
|
color = 'yellow';
|
|
break;
|
|
case 3:
|
|
label = 'Good';
|
|
color = 'green';
|
|
break;
|
|
case 4:
|
|
label = 'Strong';
|
|
color = 'green';
|
|
break;
|
|
}
|
|
|
|
return { score, label, color, feedback };
|
|
}
|