How to validate a password with yup when minimum amount of conditions met

Viewed 1735

I am trying to make a password validation using yup where at least 3 of 4 password conditions are met. I am having trouble finding an existing way to do this.

My requirements are this:

At least 8 characters It must use characters from at least three of the following four character types: • English alphabet uppercase letters (A-Z) • English alphabet lowercase letters (a-z) • Numerals (0-9) • Non-alphanumeric symbols (such as !, #, $, %)

And my yup validation (using react-hook-form) is this:

newPassword: yup
.string()
.required('Please enter a password')
.min(8, 'Password too short')
.matches(/^(?=.*[a-z])/, 'Must contain at least one lowercase character')
.matches(/^(?=.*[A-Z])/, 'Must contain at least one uppercase character')
.matches(/^(?=.*[0-9])/, 'Must contain at least one number')
.matches(/^(?=.*[!@#%&])/, 'Must contain at least one special character'),

The problem is of course this makes all 4 conditions required, when only at least 3 of the 4 need to be. Any help on solving this would be appreciated! Thank you

2 Answers

i would advise you to use the yup method yup.test() to write custom test functions like this one below when you have custom tests and you can extend it also with yup.addMethod() if you want to reuse the test function

 password: Yup.string()
      .required("Please enter a password")
      .min(8, "Password too short")
      .test("isValidPass", " is not valid", (value, context) => {
        const hasUpperCase = /[A-Z]/.test(value);
        const hasLowerCase = /[a-z]/.test(value);
        const hasNumber = /[0-9]/.test(value);
        const hasSymbole = /[!@#%&]/.test(value);
        let validConditions = 0;
        const numberOfMustBeValidConditions = 3;
        const conditions = [hasLowerCase, hasUpperCase, hasNumber, hasSymbole];
        conditions.forEach((condition) =>
          condition ? validConditions++ : null
        );
        if (validConditions >= numberOfMustBeValidConditions) {
          return true;
        }
        return false;
      })

this is a demo codesandbox with a working example

Hope so it will be help full (with Password and Confirm Password Validation) and it will catch the all Password requirements.

import * as yup from 'yup';

 const PasswordValidation = yup.object().shape({
  password: yup
    .string()
    .required('Password Required')
    .min(8, 'Password too short')
    .test(
      'isValidPass',
      'Passowrd must be 8 char (One UpperCase & One Symbol)',
      (value: any, context: any) => {
        const hasUpperCase = /[A-Z]/.test(value);
        const hasNumber = /[0-9]/.test(value);
        const hasLowerCase = /[a-z]/.test(value);
        const hasSymbole = /["!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"]/.test(value);
        let validConditions = 0;
        const numberOfMustBeValidConditions = 3;
        const conditions = [hasUpperCase, hasLowerCase, hasNumber, hasSymbole];
        conditions.forEach(condition => (condition ? validConditions++ : null));
        if (validConditions >= numberOfMustBeValidConditions) {
          return true;
        }
        return false;
      },
    ),
  confirmpassword: yup
    .string()
    .required('Confirm Password Required')
    .oneOf([yup.ref('password'), null], 'Passwords must match'),
});
Related