Fixed form (error messages too long). Consolidated tests & errors. Moved regexes to another file. Added regex to check for PII & reject pwd if true. Confirmed hashing & encryption/decryption works with top 50 languages, emojis etc (screen videos & unit tests to come).

This commit is contained in:
Joel Biddle
2023-08-25 01:44:02 +10:00
parent 688cf91eb7
commit 4d6a8f0476
14 changed files with 293 additions and 374 deletions
@@ -40,14 +40,12 @@ interface UserInfoStepProps {
type Errors = {
tooShort?: string;
tooLong?: string;
upperCase?: string;
lowerCase?: string;
number?: string;
specialChar?: string;
noLetterChar?: string;
noNumOrSpecialChar?: string;
repeatedChar?: string;
isEmail?: string;
isUrl?: string;
isBeachedPassword?: string;
escapeChar?: string;
lowEntropy?: string;
breached?: string;
};
/**
@@ -1,129 +1,87 @@
import {string} from "yup";
import { letterCharRegex, numAndSpecialCharRegex, repeatedCharRegex, escapeCharRegex, lowEntropyRegexes } from "./passwordRegexes";
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
/* eslint-disable no-param-reassign */
interface PasswordCheckProps {
password: string;
errorCheck: boolean;
setPasswordErrorTooShort: (value: boolean) => void;
setPasswordErrorTooLong: (value: boolean) => void;
setPasswordErrorUpperCase: (value: boolean) => void;
setPasswordErrorLowerCase: (value: boolean) => void;
setPasswordErrorNumber: (value: boolean) => void;
setPasswordErrorSpecialChar: (value: boolean) => void;
setPasswordErrorNoLetterChar: (value: boolean) => void;
setPasswordErrorNoNumOrSpecialChar: (value: boolean) => void;
setPasswordErrorRepeatedChar: (value: boolean) => void;
setPasswordErrorIsEmail: (value: boolean) => void;
setPasswordErrorIsUrl: (value: boolean) => void;
setPasswordErrorIsBreachedPassword: (value: boolean) => void;
setPasswordErrorEscapeChar: (value: boolean) => void;
setPasswordErrorLowEntropy: (value: boolean) => void;
setPasswordErrorBreached: (value: boolean) => void;
}
/**
* This function checks a user password with respect to some criteria.
*/
const passwordCheck = async ({
password,
setPasswordErrorTooShort,
setPasswordErrorTooLong,
setPasswordErrorUpperCase,
setPasswordErrorLowerCase,
setPasswordErrorNumber,
setPasswordErrorSpecialChar,
setPasswordErrorNoLetterChar,
setPasswordErrorNoNumOrSpecialChar,
setPasswordErrorRepeatedChar,
setPasswordErrorIsEmail,
setPasswordErrorIsUrl,
setPasswordErrorIsBreachedPassword,
setPasswordErrorEscapeChar,
setPasswordErrorLowEntropy,
setPasswordErrorBreached,
errorCheck
}: PasswordCheckProps) => {
// tooShort
if (!password || password.length < 14) {
setPasswordErrorTooShort(true);
const tests = [
{
name: "tooShort",
validator: (pwd: string) => pwd.length >= 14,
setError: setPasswordErrorTooShort,
},
{
name: "tooLong",
validator: (pwd: string) => pwd.length < 101,
setError: setPasswordErrorTooLong,
},
{
name: "noLetterChar",
validator: (pwd: string) => letterCharRegex.test(pwd),
setError: setPasswordErrorNoLetterChar,
},
{
name: "noNumOrSpecialChar",
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
setError: setPasswordErrorNoNumOrSpecialChar,
},
{
name: "repeatedChar",
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
setError: setPasswordErrorRepeatedChar,
},
{
name: "escapeChar",
validator: (pwd: string) => !escapeCharRegex.test(pwd),
setError: setPasswordErrorEscapeChar,
},
{
name: "lowEntropy",
validator: (pwd: string) => (
!lowEntropyRegexes.some(regex => regex.test(pwd))
),
setError: setPasswordErrorLowEntropy,
},
];
const isBreached = await checkIsPasswordBreached(password);
if (isBreached) {
errorCheck = true;
setPasswordErrorBreached(true);
} else {
setPasswordErrorTooShort(false);
setPasswordErrorBreached(false);
}
// tooLong
if (password.length > 100) {
setPasswordErrorTooLong(true);
errorCheck = true;
} else {
setPasswordErrorTooLong(false);
}
// upperCase
if (!/[A-Z\u0041-\u005A\u00C0-\u00D6\u00D8-\u00DE]/.test(password)) {
setPasswordErrorUpperCase(true);
errorCheck = true;
} else {
setPasswordErrorUpperCase(false);
}
// lowerCase
if (!/[a-z\u0061-\u007A\u00DF-\u00F6\u00F8-\u00FF]/.test(password)) {
setPasswordErrorLowerCase(true);
errorCheck = true;
} else {
setPasswordErrorLowerCase(false);
}
// number
if (!/[0-9]/.test(password)) {
setPasswordErrorNumber(true);
errorCheck = true;
} else {
setPasswordErrorNumber(false);
}
// specialChar
if (
!/[!@#$%^&*(),.?":{}|<>\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00D6\u00C7\u00FC\u00FB\u00F6\u00EB\u00E7\u00C7\p{Emoji}]/u.test(
password
)
) {
setPasswordErrorSpecialChar(true);
errorCheck = true;
} else {
setPasswordErrorSpecialChar(false);
}
// repeatedChar
if (
/([!@#$%^&*(),.?":{}|<>0-9A-Za-z\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00D6\u00C7\u00FC\u00FB\u00F6\u00EB\u00E7\u00C7\u003a-\u003f\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\p{Emoji}])\1\1/.test(
password
)
) {
setPasswordErrorRepeatedChar(true);
errorCheck = true;
} else {
setPasswordErrorRepeatedChar(false);
}
// isEmail
const emailSchema = string().email();
if (await emailSchema.isValid(password)) {
setPasswordErrorIsEmail(true);
errorCheck = true;
} else {
setPasswordErrorIsEmail(false);
}
// isUrl
const urlSchema = string().url();
if (await urlSchema.isValid(password)) {
setPasswordErrorIsUrl(true);
errorCheck = true;
} else {
setPasswordErrorIsUrl(false);
}
// breachedPassword
if (await checkIsPasswordBreached(password)) {
setPasswordErrorIsBreachedPassword(true);
errorCheck = true;
} else {
setPasswordErrorIsBreachedPassword(false);
for (const test of tests) {
if (!test.validator(password)) {
errorCheck = true;
test.setError(true);
} else {
test.setError(false);
}
}
return errorCheck;
@@ -1,17 +1,15 @@
import {string} from "yup"
import { letterCharRegex, numAndSpecialCharRegex, repeatedCharRegex, escapeCharRegex, lowEntropyRegexes } from "./passwordRegexes";
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
type Errors = {
tooShort?: string;
tooLong?: string;
upperCase?: string;
lowerCase?: string;
number?: string;
specialChar?: string;
noLetterChar?: string;
noNumOrSpecialChar?: string;
repeatedChar?: string;
isEmail?: string;
isUrl?: string;
isBreachedPassword?: string;
escapeChar?: string;
lowEntropy?: string;
breached?: string;
};
interface CheckPasswordParams {
@@ -23,12 +21,11 @@ interface CheckPasswordParams {
* Validate that the password [password]:
* - Contains at least 14 characters
* - Contains at most 100 characters
* - Contains at least 1 uppercase character (A-Z)
* - Contains at least 1 lowercase character (a-z)
* - Contains at least 1 number (0-9)
* - Contains at least 1 special character
* - Contains at least 1 letter character (many languages supported) (case insensitive)
* - Contains at least 1 number (0-9) or special character (emojis included)
* - Does not contain 3 repeat, consecutive characters
* - Is not an email address
* - Does not contain any escape characters/sequences
* - Does not contain PII and/or low entropy data (eg. email address, URL, phone number, DoB, SSN, driver's license, passport)
* - Is not in a database of breached passwords
*
* The function returns whether or not the password [password]
@@ -39,82 +36,64 @@ interface CheckPasswordParams {
* @param {String} obj.password - the password to check
* @param {Function} obj.setErrors - set state function to set error object
*/
const checkPassword = async ({ password, setErrors }: CheckPasswordParams): Promise<boolean> => {
const errors: Errors = {};
// tooShort
if (password.length < 14) {
errors.tooShort = "at least 14 characters";
const tests = [
{
name: "tooShort",
validator: (pwd: string) => pwd.length >= 14,
errorText: "at least 14 characters",
},
{
name: "tooLong",
validator: (pwd: string) => pwd.length < 101,
errorText: "at most 100 characters",
},
{
name: "noLetterChar",
validator: (pwd: string) => letterCharRegex.test(pwd),
errorText: "at least 1 letter character",
},
{
name: "noNumOrSpecialChar",
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
errorText: "at least 1 number or special character",
},
{
name: "repeatedChar",
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
errorText: "at most 3 repeated, consecutive characters",
},
{
name: "escapeChar",
validator: (pwd: string) => !escapeCharRegex.test(pwd),
errorText: "No escape characters allowed.",
},
{
name: "lowEntropy",
validator: (pwd: string) => (
!lowEntropyRegexes.some(regex => regex.test(pwd))
),
errorText: "Password contains sensitive data.",
},
];
const isBreached = await checkIsPasswordBreached(password);
if (isBreached) {
errors.breached = "Password was found in a data breach.";
}
// tooLong
if (password.length > 100) {
errors.tooLong = "at most 100 characters";
}
// upperCase
// this adds support for the user to select an uppercase character from many major languages
// NB. ES2018 is required to run this
if (!/[A-Z\u0041-\u005A\u00C0-\u00D6\u00D8-\u00DE]/.test(password)) {
errors.upperCase = "at least 1 uppercase character"; // most major langauges supported
}
// lowerCase
// this adds support for the user to select a lowercase character from many major languages
// NB. ES2018 is required to run this
if (!/[a-z\u0061-\u007A\u00DF-\u00F6\u00F8-\u00FF]/.test(password)) {
errors.lowerCase = "at least 1 lowercase character"; // most major langauges supported
}
// number
if (!/[0-9]/.test(password)) {
errors.number = "at least 1 number";
}
// specialChar
// this adds support for the user to select a special character from many major languages and emojis
// NB. ES2018 is required to run this
if (
!/[!@#$%^&*(),.?":{}|<>\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00D6\u00C7\u00FC\u00FB\u00F6\u00EB\u00E7\u00C7\p{Emoji}]/u.test(
password
)
) {
errors.specialChar = "at least 1 special character (emojis, symbols & non-Latin languages)";
}
// repeatedChar
// this prevents the user from selecting repeated characters from many major languages, emojis as well as numbers and symbols
// NB. ES2018 is required to run this
if (
/([!@#$%^&*(),.?":{}|<>0-9A-Za-z\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00D6\u00C7\u00FC\u00FB\u00F6\u00EB\u00E7\u00C7\u003a-\u003f\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\p{Emoji}])\1\1/.test(
password
)
) {
errors.repeatedChar = "at most 2 repeated, consecutive characters";
}
// isEmail
const emailSchema = string().email();
if (await emailSchema.isValid(password)) {
errors.isEmail = "The password cannot be an email address";
}
// isUrl
const urlSchema = string().url();
if (await urlSchema.isValid(password)) {
errors.isUrl = "The password cannot be a URL";
}
// breachedPassword
if (await checkIsPasswordBreached(password)) {
errors.isBreachedPassword =
"The new password is in a list of passwords commonly used on other websites. Please try again with a stronger password.";
for (const test of tests) {
if (test.validator && !test.validator(password)) {
errors[test.name as keyof Errors] = test.errorText;
}
}
setErrors(errors);
return Object.keys(errors).length > 0;
};
export default checkPassword;
export default checkPassword;
@@ -0,0 +1,35 @@
// This regex covers letters (case insensitive) for the top 50 most spoken languages
export const letterCharRegex = /[A-Za-z\u00C0-\u00D6\u00D8-\u00DE\u00DF-\u00F6\u00F8-\u00FF\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00C7\u00FC\u00FB\u00EB\u00E7]/u;
// This regex covers digits, special characters, symbols, and emojis.
export const numAndSpecialCharRegex = /[\d!@#$%^&*(),.?":{}|<>]|[^\p{L}\p{N}\s]/gu;
// This regex covers 3 repeated consecutive chars (incl. spaces)
export const repeatedCharRegex = /(.)\1\1\1|\s{4,}/;
// This regex covers the escape sequences as a precaution
export const escapeCharRegex = /[\n\t\r\\]/;
// This regex covers some PII and/or low entropy data
export const lowEntropyRegexes = [
// Email address
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/,
// URL (incl. subdomains, paths, top-level domains & query params)
/^(?:(?:https?|ftp):\/\/)?(?:\w+\.)?[a-zA-Z0-9.-]+\.(?:com|org|net|edu)(?:\/\S*)?(?:\?\S*)?$/,
// Date in various formats
/(\b\d{1,4}[-\/.]?\d{1,2}[-\/.]?\d{1,4}\b)|(\b\d{1,4}[-\/.]?\w{3}[-\/.]?\d{1,4}\b)/,
// Phone numbers (generalized)
/(?:\+(?:[1-9]\d{0,2})\s?)?(?:\(\d{1,4}\)\s?)?(?:\d[-.\s]?){5,}\d/,
// Passport numbers (generalized)
/\b(?:[A-Z0-9]{6,9}|[A-Z0-9]{8,9}|[A-Z0-9]{9}|[A-Z0-9]{10,11})\b/,
// Driver's license numbers (generalized)
/\b(?:[A-Z0-9]{7,10}|[A-Z0-9]{10,11}|[A-Z0-9]{7,10})\b/,
// US social security number
/\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/,
];
+64 -97
View File
@@ -30,14 +30,12 @@ export default function PasswordReset() {
const [backupKeyError, setBackupKeyError] = useState(false);
const [passwordErrorTooShort, setPasswordErrorTooShort] = useState(false);
const [passwordErrorTooLong, setPasswordErrorTooLong] = useState(false);
const [passwordErrorUpperCase, setPasswordErrorUpperCase] = useState(false);
const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false);
const [passwordErrorNumber, setPasswordErrorNumber] = useState(false);
const [passwordErrorSpecialChar, setPasswordErrorSpecialChar] = useState(false);
const [passwordErrorNoLetterChar, setPasswordErrorNoLetterChar] = useState(false);
const [passwordErrorNoNumOrSpecialChar, setPasswordErrorNoNumOrSpecialChar] = useState(false);
const [passwordErrorRepeatedChar, setPasswordErrorRepeatedChar] = useState(false);
const [passwordErrorIsEmail, setPasswordErrorIsEmail] = useState(false);
const [passwordErrorIsUrl, setPasswordErrorIsUrl] = useState(false);
const [passwordErrorIsBreachedPassword, setPasswordErrorIsBreachedPassword] = useState(false);
const [passwordErrorEscapeChar, setPasswordErrorEscapeChar] = useState(false);
const [passwordErrorLowEntropy, setPasswordErrorLowEntropy] = useState(false);
const [passwordErrorBreached, setPasswordErrorBreached] = useState(false);
const router = useRouter();
@@ -76,14 +74,12 @@ export default function PasswordReset() {
password: newPassword,
setPasswordErrorTooShort,
setPasswordErrorTooLong,
setPasswordErrorUpperCase,
setPasswordErrorLowerCase,
setPasswordErrorNumber,
setPasswordErrorSpecialChar,
setPasswordErrorNoLetterChar,
setPasswordErrorNoNumOrSpecialChar,
setPasswordErrorRepeatedChar,
setPasswordErrorIsEmail,
setPasswordErrorIsUrl,
setPasswordErrorIsBreachedPassword,
setPasswordErrorEscapeChar,
setPasswordErrorLowEntropy,
setPasswordErrorBreached,
errorCheck: false
});
@@ -227,7 +223,7 @@ export default function PasswordReset() {
</p>
<div className="mt-1 flex flex-row items-center justify-center md:mx-2 md:pb-4">
<p className="flex w-max max-w-md justify-center text-sm text-gray-400">
Make sure you save it somewhere save.
Make sure you save it somewhere safe.
</p>
</div>
<div className="mt-4 flex max-h-24 w-full items-center justify-center rounded-lg md:mt-0 md:max-h-28 md:p-2">
@@ -239,14 +235,12 @@ export default function PasswordReset() {
password,
setPasswordErrorTooShort,
setPasswordErrorTooLong,
setPasswordErrorUpperCase,
setPasswordErrorLowerCase,
setPasswordErrorNumber,
setPasswordErrorSpecialChar,
setPasswordErrorNoLetterChar,
setPasswordErrorNoNumOrSpecialChar,
setPasswordErrorRepeatedChar,
setPasswordErrorIsEmail,
setPasswordErrorIsUrl,
setPasswordErrorIsBreachedPassword,
setPasswordErrorEscapeChar,
setPasswordErrorLowEntropy,
setPasswordErrorBreached,
errorCheck: false
});
}}
@@ -256,14 +250,12 @@ export default function PasswordReset() {
error={
passwordErrorTooShort &&
passwordErrorTooLong &&
passwordErrorUpperCase &&
passwordErrorLowerCase &&
passwordErrorNumber &&
passwordErrorSpecialChar &&
passwordErrorNoLetterChar &&
passwordErrorNoNumOrSpecialChar &&
passwordErrorRepeatedChar &&
passwordErrorIsEmail &&
passwordErrorIsUrl &&
passwordErrorIsBreachedPassword
passwordErrorEscapeChar &&
passwordErrorLowEntropy &&
passwordErrorBreached
}
autoComplete="new-password"
id="new-password"
@@ -271,14 +263,12 @@ export default function PasswordReset() {
</div>
{passwordErrorTooShort ||
passwordErrorTooLong ||
passwordErrorUpperCase ||
passwordErrorLowerCase ||
passwordErrorNumber ||
passwordErrorSpecialChar ||
passwordErrorNoLetterChar ||
passwordErrorNoNumOrSpecialChar ||
passwordErrorRepeatedChar ||
passwordErrorIsEmail ||
passwordErrorIsUrl ||
passwordErrorIsBreachedPassword ? (
passwordErrorEscapeChar ||
passwordErrorLowEntropy ||
passwordErrorBreached ? (
<div className="mx-2 mt-3 mb-2 flex w-full max-w-md flex-col items-start rounded-md bg-white/5 px-2 py-2">
<div className="mb-1 text-sm text-gray-400">Password should contain:</div>
<div className="ml-1 flex flex-row items-center justify-start">
@@ -302,53 +292,30 @@ export default function PasswordReset() {
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorUpperCase ? (
{passwordErrorNoLetterChar ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${passwordErrorUpperCase ? "text-gray-400" : "text-gray-600"} text-sm`}
className={`${passwordErrorNoLetterChar ? "text-gray-400" : "text-gray-600"} text-sm`}
>
at least 1 uppercase character
at least 1 letter character
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorLowerCase ? (
{passwordErrorNoNumOrSpecialChar ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${passwordErrorLowerCase ? "text-gray-400" : "text-gray-600"} text-sm`}
className={`${passwordErrorNoNumOrSpecialChar ? "text-gray-400" : "text-gray-600"} text-sm`}
>
at least 1 lowercase character
at least 1 number or special character
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorNumber ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div className={`${passwordErrorNumber ? "text-gray-400" : "text-gray-600"} text-sm`}>
at least 1 number
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorSpecialChar ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${
passwordErrorSpecialChar ? "text-gray-400" : "text-gray-600"
} text-sm`}
>
at least 1 special character (emojis and many langauge scripts supported)
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorRepeatedChar ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
@@ -359,48 +326,48 @@ export default function PasswordReset() {
passwordErrorRepeatedChar ? "text-gray-400" : "text-gray-600"
} text-sm`}
>
at most 2 repeated, consecutive characters
at most 3 repeated, consecutive characters
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorIsEmail ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${passwordErrorIsEmail ? "text-gray-400" : "text-gray-600"} text-sm`}
>
The password cannot be an email address.
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorIsUrl ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${passwordErrorIsUrl ? "text-gray-400" : "text-gray-600"} text-sm`}
>
The password cannot be a URL.
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorIsBreachedPassword ? (
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorEscapeChar ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${
passwordErrorIsBreachedPassword ? "text-gray-400" : "text-gray-600"
passwordErrorEscapeChar ? "text-gray-400" : "text-gray-600"
} text-sm`}
>
The new password is in a list of passwords commonly used on other websites. Please
try again with a stronger password.
No escape characters allowed.
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorLowEntropy ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${passwordErrorLowEntropy ? "text-gray-400" : "text-gray-600"} text-sm`}
>
Password contains sensitive data.
</div>
</div>
<div className="ml-1 flex flex-row items-center justify-start">
{passwordErrorBreached ? (
<FontAwesomeIcon icon={faX} className="text-md mr-2.5 text-red" />
) : (
<FontAwesomeIcon icon={faCheck} className="text-md mr-2 text-primary" />
)}
<div
className={`${
passwordErrorBreached ? "text-gray-400" : "text-gray-600"
} text-sm`}
>
Password was found in a data breach.
</div>
</div>
</div>
</div>
) : (
+5 -7
View File
@@ -31,14 +31,12 @@ const client = new jsrp.client();
type Errors = {
tooShort?: string;
tooLong?: string;
upperCase?: string;
lowerCase?: string;
number?: string;
specialChar?: string;
noLetterChar?: string;
noNumOrSpecialChar?: string;
repeatedChar?: string;
isEmail?: string;
isUrl?: string;
breachedPassword?: string;
escapeChar?: string;
lowEntropy?: string;
breached?: string;
};
export default function SignupInvite() {
@@ -15,14 +15,12 @@ import { useUser } from "@app/context";
type Errors = {
tooShort?: string;
tooLong?: string;
upperCase?: string;
lowerCase?: string;
number?: string;
specialChar?: string;
noLetterChar?: string;
noNumOrSpecialChar?: string;
repeatedChar?: string;
isEmail?: string;
isUrl?: string;
isBreachedPassword?: string;
escapeChar?: string;
lowEntropy?: string;
breached?: string;
};
const schema = yup
@@ -35,14 +35,12 @@ type Props = {
type Errors = {
tooShort?: string;
tooLong?: string;
upperCase?: string;
lowerCase?: string;
number?: string;
specialChar?: string;
noLetterChar?: string;
noNumOrSpecialChar?: string;
repeatedChar?: string;
isEmail?: string;
isUrl?: string;
isBeachedPassword?: string;
escapeChar?: string;
lowEntropy?: string;
breached?: string;
};
/**