83

How to added password validation rule in the validator?

Validation rule:

The password contains characters from at least three of the following five categories:

  • English uppercase characters (A – Z)
  • English lowercase characters (a – z)
  • Base 10 digits (0 – 9)
  • Non-alphanumeric (For example: !, $, #, or %)
  • Unicode characters

How to add above rule in the validator rule?

My Code Here

// create the validation rules ------------------------
    $rules = array(
        'name'             => 'required',                        // just a normal required validation
        'email'            => 'required|email|unique:ducks',     // required and must be unique in the ducks table
        'password'         => 'required',
        'password_confirm' => 'required|same:password'           // required and has to match the password field
    );

    // do the validation ----------------------------------
    // validate against the inputs from our form
    $validator = Validator::make(Input::all(), $rules);

    // check if the validator failed -----------------------
    if ($validator->fails()) {

        // get the error messages from the validator
        $messages = $validator->messages();

        // redirect our user back to the form with the errors from the validator
        return Redirect::to('home')
            ->withErrors($validator);

    }
Maytham Fahmi
  • 31,138
  • 14
  • 118
  • 137
Bharanikumar
  • 25,457
  • 50
  • 131
  • 201

7 Answers7

150

I have had a similar scenario in Laravel and solved it in the following way.

The password contains characters from at least three of the following five categories:

  • English uppercase characters (A – Z)
  • English lowercase characters (a – z)
  • Base 10 digits (0 – 9)
  • Non-alphanumeric (For example: !, $, #, or %)
  • Unicode characters

First, we need to create a regular expression and validate it.

Your regular expression would look like this:

^.*(?=.{3,})(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[\d\x])(?=.*[!$#%]).*$

I have tested and validated it on this site. Yet, perform your own in your own manner and adjust accordingly. This is only an example of regex, you can manipulate the way you want.

So your final Laravel regex rule should be like this:

'password' => [
    'required',
    'min:6',
    'regex:/^.*(?=.{3,})(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[\d\x])(?=.*[!$#%]).*$/',
    'confirmed'
]

Note:

  1. I have tested and validated it on both the regular expression site and a Laravel 5 test environment, and it works.
  2. I have used min:6, this is optional, but it is always a good practice to have a security policy that reflects different aspects, one of which is minimum password length.
  3. I suggest you to use password confirmed to ensure user typing correct password.
  4. Within the 6 characters, our regex should contain at least 3 of a-z or A-Z and number and special character.
  5. Always test your code in a test environment before moving to production.
  6. What I have done in this answer is just example of regex password

Regarding your custom validation message for the regex rule in Laravel, here are a few links to look at:

miken32
  • 42,008
  • 16
  • 111
  • 154
Maytham Fahmi
  • 31,138
  • 14
  • 118
  • 137
  • 2
    In your regex, use a \x instead of \X since \X has no special meaning – Mazzy Dec 07 '16 at 15:06
  • 2
    Laravel 5.6 requires the validation to be provided as an array: 'password' => [ 'required', 'min:6', 'regex:/^.*(?=.{3,})(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[\d\X])(?=.*[!$#%]).*$/', 'confirmed', ], – Nik K Apr 06 '18 at 10:48
  • Can't we get rid of the `min:6` and specify that rule as part of the regex? – Barry D. Sep 21 '18 at 11:30
  • Also, you can add the validation to your service provider in the boot method like Validator::extend('strong_password', function ($attribute, $value, $parameters, $validator) { return preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*(_|[^\w])).+$/', (string)$value); }, 'Please make a strong password with at least one uppercase and lowercase letter, one number and one special character'); and then reference it like 'required|strong_password|string|min:6|confirmed' – justrusty Jan 28 '19 at 10:31
  • also in laravel 5.5 you should use array – Vahid Alvandi May 31 '19 at 07:43
  • white spaces are passing validation with this regex – Mark Jul 29 '20 at 15:22
  • At Laravel v7.25.0, PHP 7.4.9 this regex isn't working. The error "preg_match(): Compilation failed: escape sequence is invalid in character class at offset 46" is thrown. – Florian Falk Aug 26 '20 at 12:53
94

This doesn't quite match the OP requirements, though hopefully it helps. With Laravel you can define your rules in an easy-to-maintain format like so:

    $inputs = [
        'email'    => 'foo',
        'password' => 'bar',
    ];

    $rules = [
        'email'    => 'required|email',
        'password' => [
            'required',
            'string',
            'min:10',             // must be at least 10 characters in length
            'regex:/[a-z]/',      // must contain at least one lowercase letter
            'regex:/[A-Z]/',      // must contain at least one uppercase letter
            'regex:/[0-9]/',      // must contain at least one digit
            'regex:/[@$!%*#?&]/', // must contain a special character
        ],
    ];

    $validation = \Validator::make( $inputs, $rules );

    if ( $validation->fails() ) {
        print_r( $validation->errors()->all() );
    }

Would output:

    [
        'The email must be a valid email address.',
        'The password must be at least 10 characters.',
        'The password format is invalid.',
    ]

(The regex rules share an error message by default—i.e. four failing regex rules result in one error message)

James
  • 1,694
  • 14
  • 14
  • 18
    I like this solution a lot better than the ones which put all of the rules in one long regex. This seems a lot easier to understand and maintain. – Derrick Miller Mar 14 '19 at 18:42
  • looks clean while you have splitted up the Regex patterns – ManojKiran A Sep 14 '19 at 04:54
  • Aesthetics are much nicer with this answer. Someone is going to have to debug that regular expression (in accepted answer) - especially since it's obviously changing in different Larvel versions ... sure don't want it to be me! This should be accepted answer. – Charlie Dalsass Nov 23 '20 at 21:43
  • that solution allows me to adapt different password policies in the same project. almost the best practice for me. – Buraco Feb 04 '21 at 12:16
  • Totally agree that this solution looks MUCH easier to read and understand if you're not a hardcore regex guru! – leo Mar 16 '21 at 06:00
  • I think it should be a best answer which is a lot better to maintain – Yohanim Jun 14 '21 at 15:27
  • This is great, I used it, thumbs up. However, if we could also display custom message with each failing regex, it will be fantastic. – K. Shahzad Apr 26 '23 at 17:44
57

Since Laravel version 8, you can use built-in password validation:

// Require at least 8 characters...
Password::min(8)

// Require at least one letter...
Password::min(8)->letters()

// Require at least one uppercase and one lowercase letter...
Password::min(8)->mixedCase()

// Require at least one number...
Password::min(8)->numbers()

// Require at least one symbol...
Password::min(8)->symbols()

or you can chain them all


use Illuminate\Validation\Rules\Password;

$rules = [
    'password' => [
        'required',
        'string',
        Password::min(8)
            ->mixedCase()
            ->numbers()
            ->symbols()
            ->uncompromised(),
        'confirmed'
    ],
]
miken32
  • 42,008
  • 16
  • 111
  • 154
Moode Osman
  • 1,715
  • 18
  • 17
  • 2
    This is what I went with. This is the most relevant answer for 8.x. Laravel docs are clear, this is the example Laravel shows on their site. Thanks! – TGR Jun 12 '21 at 06:04
  • 1
    This is more Laravel way of doing it. Only issue with this is you cannot modify the validation messages from within the request, you need to directly edit it in the language file. – Sabin Chacko Aug 24 '22 at 07:38
  • 1
    what if the requirement only use symbols or numbers, not both. is this possible to do ? – Muzakir Nur Oct 31 '22 at 03:59
  • I'm using Laravel 10, by default I have validation rule same as this, however while user is trying to register, I want the user to have strong password having 1 symbol, number, and letter as a requirement. In my case it always accepts anything that is being typed by the user, is there an alternative way, or am I missing validation rules for this? – Two May 28 '23 at 07:07
25

A Custom Laravel Validation Rule will allow developers to provide a custom message with each use case for a better UX experience.

php artisan make:rule IsValidPassword

namespace App\Rules;

use Illuminate\Support\Str;
use Illuminate\Contracts\Validation\Rule;

class isValidPassword implements Rule
{
    /**
     * Determine if the Length Validation Rule passes.
     *
     * @var boolean
     */
    public $lengthPasses = true;

    /**
     * Determine if the Uppercase Validation Rule passes.
     *
     * @var boolean
     */
    public $uppercasePasses = true;

    /**
     * Determine if the Numeric Validation Rule passes.
     *
     * @var boolean
     */
    public $numericPasses = true;

    /**
     * Determine if the Special Character Validation Rule passes.
     *
     * @var boolean
     */
    public $specialCharacterPasses = true;

    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        $this->lengthPasses = (Str::length($value) >= 10);
        $this->uppercasePasses = (Str::lower($value) !== $value);
        $this->numericPasses = ((bool) preg_match('/[0-9]/', $value));
        $this->specialCharacterPasses = ((bool) preg_match('/[^A-Za-z0-9]/', $value));

        return ($this->lengthPasses && $this->uppercasePasses && $this->numericPasses && $this->specialCharacterPasses);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        switch (true) {
            case ! $this->uppercasePasses
                && $this->numericPasses
                && $this->specialCharacterPasses:
                return 'The :attribute must be at least 10 characters and contain at least one uppercase character.';

            case ! $this->numericPasses
                && $this->uppercasePasses
                && $this->specialCharacterPasses:
                return 'The :attribute must be at least 10 characters and contain at least one number.';

            case ! $this->specialCharacterPasses
                && $this->uppercasePasses
                && $this->numericPasses:
                return 'The :attribute must be at least 10 characters and contain at least one special character.';

            case ! $this->uppercasePasses
                && ! $this->numericPasses
                && $this->specialCharacterPasses:
                return 'The :attribute must be at least 10 characters and contain at least one uppercase character and one number.';

            case ! $this->uppercasePasses
                && ! $this->specialCharacterPasses
                && $this->numericPasses:
                return 'The :attribute must be at least 10 characters and contain at least one uppercase character and one special character.';

            case ! $this->uppercasePasses
                && ! $this->numericPasses
                && ! $this->specialCharacterPasses:
                return 'The :attribute must be at least 10 characters and contain at least one uppercase character, one number, and one special character.';

            default:
                return 'The :attribute must be at least 10 characters.';
        }
    }
}

Then on your request validation:

$request->validate([
    'email'    => 'required|string|email:filter',
    'password' => [
        'required',
        'confirmed',
        'string',
        new isValidPassword(),
    ],
]);
ahinkle
  • 2,117
  • 3
  • 29
  • 58
4

Sounds like a good job for regular expressions.

Laravel validation rules support regular expressions. Both 4.X and 5.X versions are supporting it :

This might help too:

http://www.regular-expressions.info/unicode.html

Maytham Fahmi
  • 31,138
  • 14
  • 118
  • 137
Matthew Way
  • 331
  • 1
  • 4
3

it's easy to do so with laravel 8:

 $rules = array(
    'name'             => ['required'],                        
    'email'            => ['required','email','unique:ducks'],     
    'password'         => ['required', 'confirmed',Password::min(8)
                                                   ->letters()
                                                   ->mixedCase()
                                                   ->numbers()
                                                   ->symbols()
                                                   ->uncompromised()
                           ],
);

See the doc , ( in your case you can ignore the uncompromised rule).

ali filali
  • 310
  • 4
  • 8
1
laravel 9 password validation




 $request->validate([
    'name' => 'required', 'string', 'max:255',
    'email' => 'required', 'string', 'email', 'max:255', 'unique:users',
    'password' => 'required|string|min:6|confirmed|regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{6,}$/',
 ]);