From d5a53c145974a76977cfd72a2735f7c49034b929 Mon Sep 17 00:00:00 2001 From: MiguelMLorente Date: Sun, 16 Nov 2025 18:17:03 +0100 Subject: [PATCH] Introduce password validation with zod --- src/pages/Register.tsx | 80 +++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/src/pages/Register.tsx b/src/pages/Register.tsx index 8139118..a13364f 100644 --- a/src/pages/Register.tsx +++ b/src/pages/Register.tsx @@ -1,61 +1,47 @@ import { useState } from "react"; import { + Alert, Button, Header, Input, SpaceBetween, } from "@cloudscape-design/components"; - -const PASSWORD_MAX_LENGTH = 64; -const PASSWORD_MIN_LENGTH = 8; - -const specialChars = ["|", "#", "%", "&", "/", ",", "(", ")", "="]; -const numChars = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]; - -enum PasswordErrors { - PasswordTooShort, - PasswordTooLong, - MissingNumbers, - MissingSpecialChars, - PasswordDoNotMatch, -} - -const passwordMessages: Record = { - [PasswordErrors.PasswordTooShort]: `La contraseña debe tener al menos ${PASSWORD_MIN_LENGTH} letras`, - [PasswordErrors.PasswordTooLong]: `La contraseña no puede tener más de ${PASSWORD_MAX_LENGTH} letras`, - [PasswordErrors.MissingNumbers]: `La contraseña debe tener al menos un número`, - [PasswordErrors.MissingSpecialChars]: `La contraseña debe tener al menos un simbolito`, - [PasswordErrors.PasswordDoNotMatch]: "Las contraseñas deben coincidir", -}; +import * as zod from "zod"; export const Register = () => { const [userName, setUserName] = useState(""); const [password, setPassword] = useState(""); const [passwordConfirm, setPasswordConfirm] = useState(""); - const passwordErrors: PasswordErrors[] = []; - const stringContainsSpecialChars = (stringToCheck: string) => { - return specialChars.some((char) => stringToCheck.includes(char)); - }; + const PASSWORD_MAX_LENGTH = 64; + const PASSWORD_MIN_LENGTH = 8; + const passwordSchema = zod + .string() + .min(PASSWORD_MIN_LENGTH, { + error: `La contraseña debe tener al menos ${PASSWORD_MIN_LENGTH} letras`, + }) + .max(PASSWORD_MAX_LENGTH, { + error: `La contraseña no puede tener más de ${PASSWORD_MAX_LENGTH} letras`, + }) + .refine((password) => /[0-9]/.test(password), { + error: "La contraseña debe tener al menos un número", + }) + .refine((password) => /[!@#$%^&*]/.test(password), { + error: "La contraseña debe tener al menos un símbolo", + }) + .refine((password) => password === passwordConfirm, { + error: "Las contraseñas deben coincidir", + }); - const stringContainsNumber = (stringToCheck: string) => { - return numChars.some((char) => stringToCheck.includes(char)); - }; - - if (password.length < PASSWORD_MIN_LENGTH) { - passwordErrors.push(PasswordErrors.PasswordTooShort); - } - if (password.length > PASSWORD_MAX_LENGTH) { - passwordErrors.push(PasswordErrors.PasswordTooLong); - } - if (!stringContainsSpecialChars(password)) { - passwordErrors.push(PasswordErrors.MissingSpecialChars); - } - if (!stringContainsNumber(password)) { - passwordErrors.push(PasswordErrors.MissingNumbers); - } - if (password !== passwordConfirm) { - passwordErrors.push(PasswordErrors.PasswordDoNotMatch); + let passwordErrors: string[] = []; + try { + passwordSchema.parse(password); + } catch (e: unknown) { + if (e instanceof zod.ZodError) { + passwordErrors = e.issues.map((issue) => issue.message); + } else { + throw e; + } } return ( @@ -78,12 +64,10 @@ export const Register = () => { onChange={(e) => setPasswordConfirm(e.detail.value)} placeholder="Confirm password" /> - {passwordErrors.map((error) => ( - {passwordMessages[error]} + {error} ))} - - + ); };