Created
January 3, 2022 18:28
-
-
Save simon360/a4a620644d6b4afee366898e04b3622f to your computer and use it in GitHub Desktop.
A simple function to check password quality, with some React components that demonstrate how it can be used.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Only dependency is Tailwind CSS. Could swap out the class names for your own if you'd rather. | |
// | |
// Use like this: | |
// <PasswordQuality quality={checkPasswordQuality('my$uperSecur3Password')} /> | |
/** | |
* Runs a number of quality checks against a password. Can be used to encourage the user to provide a secure password. | |
* | |
* Example return value: `{score: 0.5, "Length (12)": false, Capitals: true, Numbers: false, Symbols: true}` | |
* | |
* @param {string} password the password to check | |
* @returns {object} a quality object, with a `score` key (value between 0 and 1) and some 'check' keys (with boolean values) | |
*/ | |
function checkPasswordQuality(password: string) { | |
const qualityChecks = { | |
"Length (12)": /.{12}.*/, // 12 chars minimum | |
Capitals: /.*[A-Z].*/, // At least one uppercase letter | |
Numbers: /.*[0-9].*/, // At least one number | |
Symbols: /.*[^a-zA-Z0-9].*/, // At least one non-alphanumeric | |
}; | |
const results = Object.entries(qualityChecks).map(([key, check]) => [ | |
key, | |
check.test(password), | |
]); | |
return { | |
score: | |
results.filter(([key, result]) => result === true).length / | |
Object.entries(qualityChecks).length, | |
...Object.fromEntries(results), | |
}; | |
} | |
function PasswordQualityBar({ | |
segments, | |
filled, | |
color, | |
}: { | |
segments: number; | |
filled: number; | |
color: string; | |
}) { | |
return ( | |
<div className="flex gap-1 mt-3"> | |
{Array(segments) | |
.fill(true) | |
.map((_, i) => ( | |
<div | |
className={`h-1 rounded-sm ${ | |
i > filled - 1 ? "bg-slate-500" : color | |
} basis-full`} | |
></div> | |
))} | |
</div> | |
); | |
} | |
function PasswordQuality({ | |
quality: { score, ...reqs }, | |
}: { | |
quality: { score: number }; | |
}) { | |
const bars = [ | |
{ | |
minValue: 0, | |
text: "Not secure", | |
textColor: "text-red-700", | |
barColor: "bg-red-700", | |
}, | |
{ | |
minValue: 0.25, | |
text: "Not secure", | |
textColor: "text-red-700", | |
barColor: "bg-red-700", | |
}, | |
{ | |
minValue: 0.5, | |
text: "Moderate", | |
textColor: "text-amber-700", | |
barColor: "bg-amber-700", | |
}, | |
{ | |
minValue: 0.75, | |
text: "Good", | |
textColor: "text-green-700", | |
barColor: "bg-green-700", | |
}, | |
{ | |
minValue: 1, | |
text: "Super! 🥳", | |
textColor: "text-blue-500", | |
barColor: "bg-blue-500", | |
}, | |
]; | |
const qualityInfo = bars.find(({ minValue }) => minValue >= score)!; | |
const segments = bars.length; | |
const filled = bars.indexOf(qualityInfo) + 1; | |
const textColorSuccess = qualityInfo.textColor; | |
return ( | |
<> | |
<PasswordQualityBar | |
segments={segments} | |
filled={filled} | |
color={qualityInfo.barColor} | |
/> | |
<div className="flex justify-between mt-1 items-baseline"> | |
<strong className={`${qualityInfo.textColor} text-md`}> | |
{qualityInfo.text} | |
</strong> | |
<div className="gap-4 flex"> | |
{Object.entries(reqs).map(([key, valid]) => ( | |
<span | |
className={`text-xs flex-shrink-0 ${ | |
valid ? textColorSuccess : "text-slate-500" | |
}`} | |
> | |
{valid ? "✔" : "🆇"} {key} | |
</span> | |
))} | |
</div> | |
</div> | |
</> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment