How use react-password-checklist with zod?
Asked Answered
S

4

5

I have to use the react-password-checklist library to show the user if he is obeying the password rules declared in zod's schemaUser. However, I am not successful. obs: I'm using zod to validate the fields.

The password must have:

  • a minimum of 8 characters and a maximum of 32

  • must contain 1 uppercase letter

  • must contain 1 lowercase letter,

  • must contain 1 number

  • must contain 1 special character.

Rules and value props are mandatory in react-password-checklist. I managed to pass the value, however, the roles, I don't know how to pass because it comes from the zod.In rules i tryed userSchema.shape.password.refine.rules but the property 'shape' does not exist on type 'ZodEffects' I made the code below:

import React from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import userSchema from '../../schema/signup';
import PasswordChecklist from "react-password-checklist"
import {
  Button,
  Flex,
  Text,
  FormControl,
  FormLabel,
  Heading,
  Input,
  Stack,
  FormErrorMessage
} from '@chakra-ui/react';
export const SignUp = () => {
  const {
    register,
    handleSubmit,
    getValues,
    formState: { errors, isValid }
  } = useForm<FormData>({
    resolver: zodResolver(userSchema)
  });
 return (
    <Stack minH={'100vh'} direction={{ base: 'column', lg: 'row' }}>
      <Flex p={8} flex={1} align={'center'} justify={'center'} bg="gray.300">
        <Stack spacing={4} w={'full'} maxW={'xl'}>
          <form onSubmit={handleSubmit(handleForm)}>
            <Heading fontSize={'2xl'}>Sign in to your account</Heading>
            <FormControl id="password" isInvalid={!!errors.password}>
              <FormLabel>Password</FormLabel>
              <Input
                type="password"
                {...register('password')}
                placeholder="Insert password"
              />
              <FormErrorMessage>{errors.password?.message}</FormErrorMessage>
              <PasswordChecklist value={getValues('password')} rules={userSchema.shape.password.refine.rules} />

            </FormControl>
            <FormControl
              id="confirm-password"
              isInvalid={!!errors.confirmPassword}
            >
              <FormLabel>Confirm password</FormLabel>
              <Input
                type="password"
                {...register('confirmPassword')}
                placeholder="Confirm password"
              />
              <FormErrorMessage>
                {errors.confirmPassword?.message}
              </FormErrorMessage>
            </FormControl>
              <Button
                colorScheme={'blue'}
                variant={'solid'}
                type="submit"
                disabled={isValid}
              >
                Sign in
              </Button>
            </Stack>
          </form>
        </Stack>
      </Flex>
    </Stack>
  );
};

   

Below is the schema with zod.

const userSchema = z
   .object({
     password: z
       .string()
       .min(8, 'The password must be at least 8 characters long')
       .max(32, 'The password must be a maximun 32 characters')
       .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%&*-])[A- Za-z\d!@#$%&*-]{8,}$/),
     confirmPassword: z.string(),
   .refine((fields) => fields.password === fields.confirmPassword, {
     path: ['confirmPassword'],
     message: "Passwords don't match"
   });

Links:

React Password Checklist

Zod

Scape answered 11/8, 2023 at 19:14 Comment(0)
F
4

You will need to create your own custom validation. Here is my example.

const schema = z
  .object({
    email: z.string().email(),
    password: z.string().min(8),
  })
  .superRefine(({ password }, checkPassComplexity) => {
    const containsUppercase = (ch: string) => /[A-Z]/.test(ch);
    const containsLowercase = (ch: string) => /[a-z]/.test(ch);
    const containsSpecialChar = (ch: string) =>
      /[`!@#$%^&*()_\-+=\[\]{};':"\\|,.<>\/?~ ]/.test(ch);
    let countOfUpperCase = 0,
      countOfLowerCase = 0,
      countOfNumbers = 0,
      countOfSpecialChar = 0;
    for (let i = 0; i < password.length; i++) {
      let ch = password.charAt(i);
      if (!isNaN(+ch)) countOfNumbers++;
      else if (containsUppercase(ch)) countOfUpperCase++;
      else if (containsLowercase(ch)) countOfLowerCase++;
      else if (containsSpecialChar(ch)) countOfSpecialChar++;
    }
    if (
      countOfLowerCase < 1 ||
      countOfUpperCase < 1 ||
      countOfSpecialChar < 1 ||
      countOfNumbers < 1
    ) {
      checkPassComplexity.addIssue({
        code: "custom",
        message: "password does not meet complexity requirements",
      });
    }
  });
Freedom answered 14/11, 2023 at 0:11 Comment(0)
D
3

Thanks to Jenny Patel. I've extended a little bit.

import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";

const FormDataSchema = z
  .object({
    password: z.string(),
  })
  .superRefine(({ password }, checkPassComplexity) => {
    const containsUppercase = (ch) => /[A-Z]/.test(ch);
    const containsLowercase = (ch) => /[a-z]/.test(ch);
    const containsSpecialChar = (ch) =>
      /[`!@#$%^&*()_\-+=\[\]{};':"\\|,.<>\/?~ ]/.test(ch);
    let countOfUpperCase = 0,
      countOfLowerCase = 0,
      countOfNumbers = 0,
      countOfSpecialChar = 0;

    for (let i = 0; i < password.length; i++) {
      let ch = password.charAt(i);
      if (!isNaN(+ch)) countOfNumbers++;
      else if (containsUppercase(ch)) countOfUpperCase++;
      else if (containsLowercase(ch)) countOfLowerCase++;
      else if (containsSpecialChar(ch)) countOfSpecialChar++;
    }

    let errObj = {
      upperCase: { pass: true, message: "add upper case." },
      lowerCase: { pass: true, message: "add lower case." },
      specialCh: { pass: true, message: "add special ch." },
      totalNumber: { pass: true, message: "add number." },
    };

    if (countOfLowerCase < 1) {
      errObj = { ...errObj, lowerCase: { ...errObj.lowerCase, pass: false } };
    }
    if (countOfNumbers < 1) {
      errObj = {
        ...errObj,
        totalNumber: { ...errObj.totalNumber, pass: false },
      };
    }
    if (countOfUpperCase < 1) {
      errObj = { ...errObj, upperCase: { ...errObj.upperCase, pass: false } };
    }
    if (countOfSpecialChar < 1) {
      errObj = { ...errObj, specialCh: { ...errObj.specialCh, pass: false } };
    }

    if (
      countOfLowerCase < 1 ||
      countOfUpperCase < 1 ||
      countOfSpecialChar < 1 ||
      countOfNumbers < 1
    ) {
      checkPassComplexity.addIssue({
        code: "custom",
        path: ["password"],
        message: errObj,
      });
    }
  });

and last, I've added this under password field.

{errors.password?.message && (
                  <ul className="mt-2 text-sm text-red-400">
                    {Object.keys(errors.password.message).map((m, i) => {
                      const { pass, message } = errors.password.message[m];

                      return (
                        <li key={i}>
                          <span>{pass ? "✅" : "❌"}</span>
                          <span>{message}</span>
                        </li>
                      );
                    })}
                  </ul>
                )}
Dobbs answered 21/11, 2023 at 12:53 Comment(0)
I
0

I don't know if this might help someone out there, all you need do if you are using react-password-checklist with zod and react-hook-form is just like this below:

 const password = form.getValues("createPassword");
  const passwordAgain = form.getValues("confirmPassword");

     <PasswordChecklist
          className="text-[14px]"
          rules={["minLength", "specialChar", "number", "capital", "match"]}
          minLength={8}
          maxLength={16}
          iconSize={10}
          value={password}
          valueAgain={passwordAgain}
          messages={{
            minLength: "Password must be at least 8 characters.",
            specialChar:
              "Password must contain at least one special character.",
            number: "Password must contain at least one digit.",
            capital: "Password must contain at least one uppercase letter.",
            match: "Passwords must match!",
          }}
    

You can check the react-password-checklist docs on github for more but this is all you need to do.

Inflationary answered 23/8, 2024 at 17:38 Comment(0)
B
0

I found a simpler but equally effective version, maybe even better. Here is my code:

const formSchema = z.object({
password: z
    .string()
    .min(8)
    .regex(new RegExp('^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{8,}$'), {
        message:
            'Password must be at least 8 characters and contain an uppercase letter, lowercase letter, and number'
    })
});

I hope I was useful to you

Brownlee answered 15/10, 2024 at 21:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.