User: password

  • In this section, we implement the functionality for a user to update his password

REMARK

There is quite some similarity with the approach/code used in the previous section for updating/editing a user's profile

Preparation

Create controller

  • Create a new controller class PasswordController.php in the folder app/Http/Controllers/User
    • Run the command php artisan make:controller User/PasswordController
  • Add the methods edit() and update() to the controller
class PasswordController extends Controller
{
    // Edit user password
    public function edit()
    {
        return view('user.password');
    }

    // Update and encrypt user password
    public function update(Request $request)
    {
        // Validate $request

        // Update encrypted user password in the database and redirect to previous page
        return back();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Basic view

  • Make a basic view password.blade.php inside this folder resources/views/user
  • Open the file resources/views/user/password.blade.php and update the view
    • The file contains a form and includes the shared alert sub-view to display feedback
@extends('layouts.template')

@section('title', 'Update password')

@section('main')
    <h1>New password</h1>
    @include('shared.alert')
    <form action="/user/password" method="post">
        @csrf
        <div class="form-group">
            <label for="current_password">Current password</label>
            <input type="password" name="current_password" id="current_password"
                   class="form-control @error('current_password') is-invalid @enderror"
                   placeholder="Current password"
                   value="{{ old('current_password') }}"
                   required>
            @error('current_password')
            <div class="invalid-feedback">{{ $message }}</div>
            @enderror
        </div>
        <div class="form-group">
            <label for="password">New password</label>
            <input type="password" name="password" id="password"
                   class="form-control @error('password') is-invalid @enderror"
                   placeholder="New password"
                   value="{{ old('password') }}"
                   minlength="8"
                   required>
            @error('password')
            <div class="invalid-feedback">{{ $message }}</div>
            @enderror
        </div>
        <div class="form-group">
            <label for="password_confirmation">Confirm new password</label>
            <input type="password" name="password_confirmation" id="password_confirmation"
                   class="form-control"
                   placeholder="Confirm new password"
                   value="{{ old('password_confirmation') }}"
                   minlength="8"
                   required>
        </div>
        <button type="submit" class="btn btn-success">Update password</button>
    </form>
@endsection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

Add routes

  • Open routes/web.php and add two new routes
    • One GET route for showing the form
    • One POST route for updating the password




 
 


Route::redirect('user', '/user/profile');
Route::middleware(['auth'])->prefix('user')->group(function () {
    Route::get('profile', 'User\ProfileController@edit');
    Route::post('profile', 'User\ProfileController@update');
    Route::get('password', 'User\PasswordController@edit');
    Route::post('password', 'User\PasswordController@update');
});
1
2
3
4
5
6
7

Form validation

  • Add (server-side) form validation to PasswordController.php
    • The current password (current_password) is required
    • The new password (password) is required, must be at least 8 characters long (min:8) and must be confirmed
  • If the validation fails, the user will automatically be redirected to the previous page
  • If the validation is successful, the user will (also) be redirected back and a message is flashed to the session to be displayed on top of the page



 
 
 
 


 



public function update(Request $request)
{
    // Validate $request
    $this->validate($request,[
        'current_password' => 'required',
        'password' => 'required|min:8|confirmed',
    ]);

    // Update encrypted user password in the database and redirect to previous page
    session()->flash('success', 'Your password has been updated');
    return back();
}
1
2
3
4
5
6
7
8
9
10
11
12

REMARKS

  • For an input field to be confirmed, the field under validation must have a matching field with the postfix _confirmation
    • For example, if the field under validation is password, a matching password_confirmation field must be present in the form
  • You don't need validation rules on the password_confirmation field
    • If the confirmation fails, the error will be displayed on the password field, not on the password_confirmation field

Password confirmation error

REMARK

The values old('password') and old('password_confirmation') are empty/not working because Laravel protects the corresponding input fields (password and password_confirmation) and doesn't flash their values to the session!

Update the database

REMARKS

  • Remember that the passwords (e.g. user1234) are hashed (with bcrypt) before they were inserted into the database
  • Notice that if the same password is hashed several times, the results will differ because a random salt (included in the resulting hash) is used in the bcrypt algorithm
  • The hashed passwords look like this: $2y$10$wLqY06r6o9itLGjIb6Jg6OF1.Ype94l2qdtICYZlf5sAt0kWprpra. $2y indicates the version of the used bcrypt algorithm, and $10 denotes the number of rounds. The rest of the hash string is the salt and the resulting hash itself.
  • Find the logged in user by his id (auth()->id())
  • Check if the hashed value of $request->current_password matches the value in the database ($user->password) by using the check() method on Laravel's Hash facade
    • If not, send the user back with an error message
    • If there is a match, use the method make() to hash $request->password and save the result to the database









 
 
 
 
 
 
 




public function update(Request $request)
{
    // Validate $request
    $this->validate($request, [
        'current_password' => 'required',
        'password' => 'required|min:8|confirmed',
    ]);

    // Update encrypted user password in the database and redirect to previous page
    $user = User::findOrFail(auth()->id());
    if (!Hash::check($request->current_password, $user->password)) {
        session()->flash('danger', "Your current password doesn't mach the password in the database");
        return back();
    }
    $user->password = Hash::make($request->password);
    $user->save();
    session()->flash('success', 'Your password has been updated');
    return back();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Current password faulty

Password changed

EXERCISE: Logout after password change (optional)

  • Implement the following (alternative) behavior after a password is successfully changed:
    • Log the user out
    • Send him back to the login page
Last Updated: 4/4/2020, 7:29:40 AM