fix: align password validation with backend ASCII policy (WealthLedger/backend#60) (#89)
Frontend verify / commits (push) Skipped
Frontend verify / commits (pull_request) Failing after 40s
Frontend verify / security (pull_request) Skipped
Frontend verify / verify (pull_request) Skipped
Frontend verify / security (push) Successful in 11s
Frontend verify / verify (push) Failing after 7m35s
Frontend verify / commits (push) Skipped
Frontend verify / commits (pull_request) Failing after 40s
Frontend verify / security (pull_request) Skipped
Frontend verify / verify (pull_request) Skipped
Frontend verify / security (push) Successful in 11s
Frontend verify / verify (push) Failing after 7m35s
This commit was merged in pull request #89.
This commit is contained in:
@@ -7,8 +7,10 @@ const EMAIL_PART_COUNT = 2;
|
||||
const ASCII_SPACE = 32;
|
||||
const ASCII_DELETE = 127;
|
||||
const MAX_EMAIL_LENGTH = 254;
|
||||
const MIN_PASSWORD_BYTES = 12;
|
||||
const MIN_PASSWORD_LENGTH = 8;
|
||||
const MAX_PASSWORD_BYTES = 72;
|
||||
// Printable ASCII keeps characters == bytes, within the bcrypt 72-byte limit.
|
||||
const ASCII_TILDE = 126;
|
||||
|
||||
function isEmailCharacterInvalid(character: string): boolean {
|
||||
const codePoint = character.codePointAt(0);
|
||||
@@ -20,6 +22,14 @@ function isEmailCharacterInvalid(character: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function isPrintableAscii(value: string): boolean {
|
||||
return value.split("").every((character) => {
|
||||
const code = character.codePointAt(0) ?? 0;
|
||||
|
||||
return code >= ASCII_SPACE && code <= ASCII_TILDE;
|
||||
});
|
||||
}
|
||||
|
||||
const email = z
|
||||
.string()
|
||||
.trim()
|
||||
@@ -33,28 +43,42 @@ const email = z
|
||||
{ error: validationMessage("auth.validation.invalidEmail") },
|
||||
);
|
||||
|
||||
const password = z
|
||||
// Login stays lenient: accounts created under an older policy must still sign in.
|
||||
const loginPassword = z
|
||||
.string()
|
||||
.min(1)
|
||||
.refine(
|
||||
value =>
|
||||
new TextEncoder().encode(value).length >= MIN_PASSWORD_BYTES
|
||||
&& new TextEncoder().encode(value).length <= MAX_PASSWORD_BYTES,
|
||||
{ error: validationMessage("auth.validation.invalidPasswordLength") },
|
||||
value => new TextEncoder().encode(value).length <= MAX_PASSWORD_BYTES,
|
||||
{ error: validationMessage("auth.validation.passwordTooLong") },
|
||||
);
|
||||
|
||||
const registerPassword = z
|
||||
.string()
|
||||
.min(MIN_PASSWORD_LENGTH, {
|
||||
error: validationMessage("auth.validation.invalidPasswordLength"),
|
||||
})
|
||||
.max(MAX_PASSWORD_BYTES, {
|
||||
error: validationMessage("auth.validation.invalidPasswordLength"),
|
||||
})
|
||||
.refine(isPrintableAscii, {
|
||||
error: validationMessage("auth.validation.invalidPasswordLength"),
|
||||
});
|
||||
|
||||
const loginSchema = z.object({
|
||||
email,
|
||||
password,
|
||||
password: loginPassword,
|
||||
passwordConfirmation: z.string().optional(),
|
||||
});
|
||||
|
||||
const registerSchema = loginSchema.refine(
|
||||
value => value.password === value.passwordConfirmation,
|
||||
{
|
||||
error: validationMessage("auth.validation.passwordsDoNotMatch"),
|
||||
path: ["passwordConfirmation"],
|
||||
},
|
||||
);
|
||||
const registerSchema = loginSchema
|
||||
.extend({ password: registerPassword })
|
||||
.refine(
|
||||
value => value.password === value.passwordConfirmation,
|
||||
{
|
||||
error: validationMessage("auth.validation.passwordsDoNotMatch"),
|
||||
path: ["passwordConfirmation"],
|
||||
},
|
||||
);
|
||||
|
||||
type AuthFormValues = z.input<typeof loginSchema>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user