How to Update User Password in Laravel 5

It is based on password hash equality matching

Modern web applications with user database very often require password update functionality.
The logic this functionality requires is as follows:

  1. User is offered to type three passwords on HTML page. These are current password used for logging in, new password to update to and its confirmation.
  2. When three passwords are submit to the server, new password gets compared with its confirmation to prevent potential typing mistakes.
  3. If new password is equal to its confirmation, current password typed gets compared for equality with authenticated user password stored in the database. And, optionally, current password typed can be compared to a new password, ensuring they are different – there is no reason to update user password to the same one, right?
  4. If password typed and authenticated user password stored in the database are equal, authenticated user database record gets updated with a new password.

How to Update User Password in Laravel 5

Laravel doesn't offer this functionality out of the box, therefore it shall be implemented on your own. Probably, the most accurate way to work out password update functionality is to use existing validation rules Laravel implements. Plus, create a custom validation rule to compare current password typed with authenticated user password stored in the database.

Defining a Custom Validator

Create a HashValidator.php file inside app/Http/Validators directory:

<?php namespace App\Http\Validators;

use \Illuminate\Validation\Validator;
use Hash;

class HashValidator extends Validator {

  public function validateHash($attribute, $value, $parameters) {
    return Hash::check($value, $parameters[0]);
  }
}

HashValidator class implements a public validateHash method, that compares plain password with an encrypted version of it. This method name begins with a word "validate" and follows with a name of the validation rule that is being implemented. Further on we are going to use plain "hash" string definition when adding error message for password hash validation.

It is important to note, that equality matching between password typed by hand and encrypted version of it, shall be done using Hash facade.
Would be wrong to compare two encrypted passwords directly, because Hash:make method always generates different results, even the same strings gets encrypted.

Newly created class doesn't get loaded by Composer automatically. It is required to run composer update command in the Terminal to let Composer auto load the class making it available for includes via use keyword.

Registering Custom Validator Inside AppServiceProvider

Open app/Providers/AppServiceProvider.php and register newly created custom validator under public boot method, AppServiceProvider class implements. Make sure to include App\Http\Validators\HashValidator and Validator through use keyword right after namespace definition:

<?php namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Validator;
use App\Http\Validators\HashValidator;

class AppServiceProvider extends ServiceProvider {

  /**
   * Bootstrap any application services.
   * @return void
   */
  public function boot() {
    Validator::resolver(function($translator, $data, $rules, $messages) {
      return new HashValidator($translator, $data, $rules, $messages);
  });
}

When your new password hash equality validator is registered, you can use it as any other validation rule Laravel has implementation of.

However, before that would be respectful to define an error message Validator facade reveals when our custom validation fails.

Custom Validator Error Message

Each custom validation rule must have a respectful error message defined. Otherwise, something like “validation.hash” will be stored in $validation->errors() array when you decide to display these errors on HTML page.

Open resources/lang/en/validation.php file (plus the same file under other language directories, if you have any) and add new error message into the first level of the array:

<?php
return [
  'accepted'    => 'The :attribute must be accepted.',
  'active_url'  => 'The :attribute is not a valid URL.',
  ...

  'hash'        => 'The :attribute doesn\'t match current password.',

  ...

Use Custom Validation Rule in the Controller

Now, when everything required is set up, feel free to use new validation rule inside the controller that gets triggered, when three passwords are submitted from the client to the server.

If documenting our new password hash equality validation rule in Laravel style, here is how it would look like:

hash:encrypted_match
The field under validation must have a value equals to its encrypted_match.

<?php namespace App\Http\Controllers;
  use Auth;
  use Hash;
  use Request;
  use Validator;
  ...

  $user = Auth::user();

  $validation = Validator::make(Request::all(), [

    // Here's how our new validation rule is used.
    'password' => 'hash:' . $user->password,
    'new_password' => 'required|different:password|confirmed'
  ]);

  if ($validation->fails()) {
    return redirect()->back()->withErrors($validation->errors());
  }

  $user->password = Hash::make(Request::input('new_password'));
  $user->save();

  return redirect()->back()
    ->with('success-message', 'Your new password is now set!');
  ...

All good, user password update functionality is done!

How it is made

Now let's quickly recap what we have made to set it up:

  1. We understood implementation logic of password update functionality.
  2. Programmed a custom validator class that defines a new validation rule through its validateHash public method. The method compares two values for equality using Hash facade. These are current password typed by the user and its encrypted version stored inside database.
  3. Registered newly created class under AppServiceProvider, extending Laravel's existing validation rules.
  4. Added custom error message for our new validation rule.
  5. And understood how to use new hash:encrypted_match validation rule validating request data under the controller that is triggered, when form values are submitted from HTML page to the server.
#