javascriptnode.jsregexvalidationjoi

How do I validate a password using Joi to ensure that it contains 2 numbers, 2 special characters, 2 uppercase letters, and 2 lowercase letters?


Is there a way to do this using Joi?

For example:

    Joi.string()
      .required()
      .min(8)
      .max(16)
      .pattern(/(?=(?:.*[a-z]){2,16}).+/)
      .pattern(/(?=(?:.*[A-Z]){2,16}).+/)
      .pattern(/(?=(?:.*[0-9]){2,16}).+/)
      .pattern(/(?=(?:.*[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]){2,16}).+/)
      .messages({
        'string.base': 'Username must be of type string',
        'string.regex': 'Username must contain at least two lower-case letters',
        'string.regex2': 'Username must contain at least two upper-case letters',
        'string.regex3': 'Username must contain at least two numbers',
        'string.regex4': 'Username must contain at least two special characters',
        'string.min': 'Username must be at least 8 characters long',
        'string.max': 'Username must be no more than 16 characters long',
        'string.empty': 'Username is a required field',
      }),
    })
  });

The only problem I seem to be running into is getting back a good error message for the regular expressions. What should the keys be for messages? I would also like to see all failed validations, not just the first one that fails? Is there a better approach to this?


Solution

  • const passwordSchema: ObjectSchema = Joi.object().keys({
      password: Joi.string()
        .min(8)
        .max(16)
        .pattern(/(?=(?:.*[a-z]){2,16}).+/, 'lowercase')
        .pattern(/(?=(?:.*[A-Z]){2,16}).+/, 'uppercase')
        .pattern(/(?=(?:.*[0-9]){2,16}).+/, 'number')
        .pattern(/(?=(?:.*[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]){2,16}).+/, 'special')
        .required()
        .error((errors) => {
          errors.forEach((err) => {
            switch (err.code) {
              case 'string.base':
              case 'string.empty':
              case 'any.required':
              default:
                err.message = defaultErrorMessage;
                break;
              case 'string.min':
                err.message = minLengthMessage;
                break;
              case 'string.max':
                err.message = maxLengthMessage;
                break;
              case 'string.pattern.name':
                switch (err.local.name) {
                  case 'lowercase':
                    err.message = 'must contain at least two lower-case letters';
                    break;
                  case 'uppercase':
                    err.message = 'must contain at least two lower-case letters';
                    break;
                  case 'number':
                    err.message = 'must contain at least two numbers';
                    break;
                  case 'special':
                    err.message = 'must contain at least two special characters';
                    break;
                }
                break;
            }
          });
    
          return errors;
        }),
    

    Then to get all messages:

    passwordSchema.validate(data, { abortEarly: false });