w3resource

Laravel (5.7) Password Reset

Laravel Authentication Basics

In this section, we are going to use artisan to scaffold the authentication.

Run the following command

php artisan make:auth

HERE,

The above command will generate all of the necessary routes and controllers required for a complete authentication system.

Customizing Laravel Authentication Routes

The make:auth command modified our /routes/web.php routes and added the following line

Auth::routes();

HERE,

The routes(); method generates all of the required authentication routes for us.

The method routes(); is implemented in core of the framework in the file

/vendor/laravel/framework/src/Illuminate/Routing/Router.php

public function auth()
{
    // Authentication Routes...
    $this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
    $this->post('login', 'Auth\LoginController@login');
    $this->post('logout', 'Auth\LoginController@logout')->name('logout');

    // Registration Routes...
    $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
    $this->post('register', 'Auth\RegisterController@register');

    // Password Reset Routes...
    $this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm');
    $this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
    $this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');
    $this->post('password/reset', 'Auth\ResetPasswordController@reset');
}

As you can see from the above code, the URL paths are hard-coded. This works fine for a basic application but we want our routes to be prefixed with the admin.

In addition to the admin prefix, we do not want to include the register routes for admin panel users and the password reset route does not have a name.

We can change the paths in the core of the framework and our application would work just fine but what happens when you upgrade the version of the Laravel?

You will most likely have your changes overridden and have to remember which files you modified. The alternative is to write your own custom routes in /routes/web.php and leave the core file in the framework the way it is.

Replace

Auth::routes 

with
Route::group(['prefix' => 'admin','namespace' => 'Auth'],function(){
    // Authentication Routes...
    Route::get('login', 'LoginController@showLoginForm')->name('login');
    Route::post('login', 'LoginController@login');
    Route::post('logout', 'LoginController@logout')->name('logout');

    // Password Reset Routes...
    Route::get('password/reset', 'ForgotPasswordController@showLinkRequestForm')->name('password.reset');
    Route::post('password/email', 'ForgotPasswordController@sendResetLinkEmail')->name('password.email');
    Route::get('password/reset/{token}', 'ResetPasswordController@showResetForm')->name('password.reset.token');
    Route::post('password/reset', 'ResetPasswordController@reset');
});

HERE,

Route::group(['prefix' => 'admin','namespace' =>

'Auth'],function(){} defines a group route with the prefix of admin and namespace Auth.

Route::get, Route::post replicates the routes generated by artisan make:auth command with the exception of routes required to register a new user.

Note: we have also set route names for password reset routes

Customizing Laravel Authentication Views

Scaffolding generated login and password reset views. We want to be able to work with the login views from our admin template.

The authentication views are located in /resources/views/auth

Replace the contents of /resources/views/auth/login.php with the following

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <!-- Meta, title, CSS, favicons, etc. -->
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <title>Larashop Admin Login</title>

        <!-- Bootstrap -->
        <link href="{{asset('admin/css/bootstrap.min.css')}}" rel="stylesheet">
        <!-- Font Awesome -->
        <link href="{{asset('admin/css/font-awesome.min.css')}}" rel="stylesheet">
        <!-- NProgress -->
        <link href="{{asset('admin/css/nprogress.css')}}" rel="stylesheet">
        <!-- Animate.css -->
        <link href="{{asset('admin/css/animate.min.css')}}" rel="stylesheet">

        <!-- Custom Theme Style -->
        <link href="{{asset('admin/css/custom.min.css')}}" rel="stylesheet">
    </head>

    <body class="login">
        <div>
            <div class="login_wrapper">
                <div class="animate form login_form">
                    <section class="login_content">
                        <form role="form" method="POST" action="{{ route('login') }}">
                            <h1>Larashop | Login</h1>
                            {{ csrf_field() }}
                            <div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
                                @if ($errors->has('email'))
                                <span class="help-block"><strong>{{ $errors->first('email') }}</strong></span>
                                @endif
                                <input type="text" class="form-control" id="email" name="email" placeholder="Email"/>
                            </div>
                            <div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
                                @if ($errors->has('password'))
                                <span class="help-block"><strong>{{ $errors->first('password') }}</strong></span>
                                @endif
                                <input type="password" class="form-control" id="password" name="password" placeholder="Password"/>
                            </div>
                            <div>
                                <button type="submit" class="btn btn-default submit">Login</button>
                                <a class="reset_pass" href="{{route('password.reset')}}">Lost your password?</a>
                            </div>

                            <div class="clearfix"></div>

                            <div class="separator">

                                <div class="clearfix"></div>
                                <br />

                                <div>
                                    <h1><i class="fa fa-paw"></i> Larashop Admin Panel</h1>
                                    <p>©2017 All Rights Reserved. Brough to you by <a href="https://w3resource.com" target="_blank">Kode Blog Tutorials</a></p>
                                </div>
                            </div>
                        </form>
                    </section>
                </div>
            </div>
        </div>
    </body>
</html>

HERE,

<form role="form" method="POST" action="{{ route('login') }}"> sets the form method to POST and the action to the path of the named route login

{{ csrf_field() }} adds the cross site request forgery token

<input type="text" class="form-control" id="email" name="email" placeholder="Email"/> defines the email field with the id and name of email

<input type="password" class="form-control" id="password" name="password" placeholder="Password"/> defines the password field with the id and name of password

<button type="submit" class="btn btn-default submit">Login</button> submits the form

<a class="reset_pass" href="{{route('password.reset')}}">Lost your password?</a> displays the password reset link. The password reset path uses the named route password.reset. this makes it easy to update the password reset link without changing anything in the views.

That is all we need to login in.

Let's now look at the password views in

/resources/views/auth/passwords. We have two views namely;

email.blade.php- used to display the form that asks the user for the email to use when sending the reset link.

reset.blade.php - used to display the form that the user will use to reset the password.

Replace the contents of email.blade.php with the following

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <!-- Meta, title, CSS, favicons, etc. -->
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <title>Larashop Admin Login</title>

        <!-- Bootstrap -->
        <link href="{{asset('admin/css/bootstrap.min.css')}}" rel="stylesheet">
        <!-- Font Awesome -->
        <link href="{{asset('admin/css/font-awesome.min.css')}}" rel="stylesheet">
        <!-- NProgress -->
        <link href="{{asset('admin/css/nprogress.css')}}" rel="stylesheet">
        <!-- Animate.css -->
        <link href="{{asset('admin/css/animate.min.css')}}" rel="stylesheet">

        <!-- Custom Theme Style -->
        <link href="{{asset('admin/css/custom.min.css')}}" rel="stylesheet">
    </head>

    <body class="login">
        <div>
            <a class="hiddenanchor" id="signup"></a>
            <a class="hiddenanchor" id="lostpassword"></a>

            <div class="login_wrapper">
                <div class="animate form login_form">
                    <section class="login_content">
                        @if (session('status'))
                        <div class="alert alert-success">
                            {{ session('status') }}
                        </div>
                        @endif

                        <form class="form-horizontal" role="form" method="POST" action="{{ route('password.email') }}">
                            <h1>Password Reset</h1>
                            {{ csrf_field() }}

                            <div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
                                @if ($errors->has('email'))
                                <span class="help-block"><strong>{{ $errors->first('email') }}</strong></span>
                                @endif
                                <input type="text" class="form-control" id="email" name="email" placeholder="Email"/>
                            </div>

                            <div>
                                <button type="submit" class="btn btn-default submit">Send Password Reset Link</button>
                                <a class="reset_pass" href="{{route('login')}}">Login</a>
                            </div>

                            <div class="clearfix"></div>

                            <div class="separator">

                                <div class="clearfix"></div>
                                <br />

                                <div>
                                    <h1><i class="fa fa-paw"></i> Larashop Admin Panel</h1>
                                    <p>©2017 All Rights Reserved.</p>
                                </div>
                            </div>
                        </form>
                    </section>
                </div>
            </div>
        </div>
    </body>
</html>

HERE,

@if (session('status')) checks if the session has status set and displays it using bootstrap alerts.

<form class="form-horizontal" role="form" method="POST" action="{{ route('password.email') }}"> sets the form method to POST and action to the path of the named route password.email

{{ csrf_field() }} adds the cross site forgery fields

<input type="text" class="form-control" id="email" name="email" placeholder="Email"/>adds the input box email that the use will user to enter the email address where the reset link should be sent.

<button type="submit" class="btn btn-default submit">Send Password Reset Link</button> submits the form.

<a class="reset_pass" href="{{route('login')}}">Login</a> includes a login link on the email reset page in case the user decides to go back to the login page.

Replace the contents of reset.blade.php with the following

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <!-- Meta, title, CSS, favicons, etc. -->
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <title>Larashop Admin | Password Reset</title>

        <!-- Bootstrap -->
        <link href="{{asset('admin/css/bootstrap.min.css')}}" rel="stylesheet">
        <!-- Font Awesome -->
        <link href="{{asset('admin/css/font-awesome.min.css')}}" rel="stylesheet">
        <!-- NProgress -->
        <link href="{{asset('admin/css/nprogress.css')}}" rel="stylesheet">
        <!-- Animate.css -->
        <link href="{{asset('admin/css/animate.min.css')}}" rel="stylesheet">

        <!-- Custom Theme Style -->
        <link href="{{asset('admin/css/custom.min.css')}}" rel="stylesheet">
    </head>

    <body class="login">
        <div>
            <a class="hiddenanchor" id="signup"></a>
            <a class="hiddenanchor" id="lostpassword"></a>

            <div class="login_wrapper">
                <div class="animate form login_form">
                    <section class="login_content">
                        @if (session('status'))
                        <div class="alert alert-success">
                            {{ session('status') }}
                        </div>
                        @endif
                        <form role="form" method="POST" action="{{ route('password.reset') }}">
                            <h3>Reset Password</h3>

                            {{ csrf_field() }}

                            <input type="hidden" name="token" value="{{ $token }}">

                            <div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
                                @if ($errors->has('email'))
                                <span class="help-block"><strong>{{ $errors->first('email') }}</strong></span>
                                @endif
                                <input type="text" class="form-control" id="email" name="email" placeholder="Email"/>
                            </div>

                            <div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
                                @if ($errors->has('password'))
                                <span class="help-block"><strong>{{ $errors->first('password') }}</strong></span>
                                @endif
                                <input type="password" class="form-control" id="password" name="password" placeholder="Password"/>
                            </div>

                            <div class="form-group{{ $errors->has('password_confirmation') ? ' has-error' : '' }}">
                                @if ($errors->has('password_confirmation'))
                                <span class="help-block"><strong>{{ $errors->first('password_confirmation') }}</strong></span>
                                @endif
                                <input type="password_confirmation" class="form-control" id="password_confirmation" name="password_confirmation" placeholder="Confirm Password"/>
                            </div>

                            <div>
                                <button type="submit" class="btn btn-default submit">Reset Password</button>
                            </div>

                            <div class="clearfix"></div>

                            <div class="separator">

                                <div class="clearfix"></div>
                                <br />

                                <div>
                                    <h1><i class="fa fa-paw"></i> Larashop Admin Panel</h1>
                                    <p>©2017 All Rights Reserved.</p>
                                </div>
                            </div>
                        </form>
                    </section>
                </div>
            </div>
        </div>
    </body>
</html>

HERE,

The explanation for reset.blade.php is pretty much the same as the other views that we covered earlier on.

Custom Email Reset Notification

Why do we need to customize the reset password email notification? Because the URL passet to reset password is hard coded to domain/password/reset. We want to be able to use the path domain/admin/password/reset.

We will use artisan to make our life easier. Run the following command

php artisan make:notification LarashopAdminResetPassword

the above command will create a Notifications directory in app directory. It will also create the file

app/Notifications/LarashopAdminResetPassword.php

Replace the contents of LarashopAdminResetPassword.php with the following

<?php

namespace Larashop\Notifications;

use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;

class LarashopAdminResetPassword extends Notification
{

    public function __construct($token)
    {
        $this->token = $token;
    }


    public function via($notifiable)
    {
        return ['mail'];
    }

    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->line('You are receiving this email because we received a password reset request for your account.')
            ->action('Reset Password', route('password.reset.token',['token' => $this->token]))
            ->line('If you did not request a password reset, no further action is required.');
    }
}

HERE,

The method public function toMail($notifiable){...} uses the named route password.reset.token route('password.reset.token',['token' => $this->token]) to create the path for the password reset token.

Laravel sending fake SMTP emails

In this section, we will look at how to usemailtrap.io to send emails in Laravel. In a nutshell, you will create an

account with mailtrap (free version with a limitation 50 mails in the inbox), and configure the email configurations. All emails sent from the app will be sent to your mailtrap inbox. Mailtrap is for development purposes only.

Visithttps://mailtrap.io/ to create a free account

Once you are logged in, you should be able to see the demo mailbox

The settings tab will present you with the username and password that you will need to configure in mailtrap.

Open .env file

Set the following mail settings

MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=replace_with_your_username
MAIL_PASSWORD=replace_with_your_password

Default Login account

We will use tinker to create a new user account

Run the artisan commands

$user = new Larashop\Models\User
$user->name = "Rodrick Kazembe"
$user->password = bcrypt('ceasar')
$user->save()

You can now try to login using the above account details that we just created. You can also try out other features such as password reset.

Previous: Laravel (5.7) Email Verification Tutorial Example
Next: Laravel (5.7) Artisan Console



Follow us on Facebook and Twitter for latest update.