Skip to content

Instantly share code, notes, and snippets.

@simon360
Created January 3, 2022 18:28
Show Gist options
  • Save simon360/a4a620644d6b4afee366898e04b3622f to your computer and use it in GitHub Desktop.
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.
// 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