Access all tutorials in sprocket icon.
June 16, 2015 from author Bill Keck.
How to Setup Forgot Password in Laravel 5.1
Ok, so in my last tutorial, How to Make User Login and Registration Laravel 5.1, we setup a basic user registration and login, but we left the forgot password functionality out because the tutorial was going too long.
In Laravel 5.1, they are not including the views you need serve forgot password function out of the box. The controller and traits are in place, however.
So all we need to do is make sure that we have the route set and the proper views, and it will work perfectly.
The Route
You should have a route in app/Http/routes.php file as follows:
Route::controllers([
'password' => 'Auth\PasswordController',
]);
If you still have the route the old AuthController, you can go ahead and delete the Auth\Authcontroller line, we will not be using it.
With this type of route, if we have for example, a getEmail function on our controller, the route will use the verb get from the function name to figure out what kind of request we are looking for.
We abandoned this approach for the login and registration controllers because we wanted a simpler approach. But since forgot password functionality is a little complicated, I’m leaving well-enough alone, especially since I don’t anticipate having to work on it in the future. It doesn’t seem likely to change, whereas I know for my registration, it could easily be different in the future, especially if I hook up social auth to it.
Ok, so we have our route in place, let’s look at the controller.
The Controller
The path is app/Http/Controllers/Auth/PasswordController.php.
It should look like this:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Create a new password controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
}
So where are all the actions? In the ResetsPasswords trait of course. Frankly, I don’t understand why it’s done this way. It doesn’t seem likely that the methods in the trait would be used for anything other than the forgot password feature.
Perhaps they were trying to keep the controller skinny.
The Trait
Here’s what the trait looks like:
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Password;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
trait ResetsPasswords
{
/**
* Display the form to request a password reset link.
*
* @return \Illuminate\Http\Response
*/
public function getEmail()
{
return view('auth.password');
}
/**
* Send a reset link to the given user.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function postEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$response = Password::sendResetLink($request->only('email'), function (Message $message) {
$message->subject($this->getEmailSubject());
});
switch ($response) {
case Password::RESET_LINK_SENT:
return redirect()->back()->with('status', trans($response));
case Password::INVALID_USER:
return redirect()->back()->withErrors(['email' => trans($response)]);
}
}
/**
* Get the e-mail subject line to be used for the reset link email.
*
* @return string
*/
protected function getEmailSubject()
{
return isset($this->subject) ? $this->subject : 'Your Password Reset Link';
}
/**
* Display the password reset view for the given token.
*
* @param string $token
* @return \Illuminate\Http\Response
*/
public function getReset($token = null)
{
if (is_null($token)) {
throw new NotFoundHttpException;
}
return view('auth.reset')->with('token', $token);
}
/**
* Reset the given user's password.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function postReset(Request $request)
{
$this->validate($request, [
'token' => 'required',
'email' => 'required|email',
'password' => 'required|confirmed',
]);
$credentials = $request->only(
'email', 'password', 'password_confirmation', 'token'
);
$response = Password::reset($credentials, function ($user, $password) {
$this->resetPassword($user, $password);
});
switch ($response) {
case Password::PASSWORD_RESET:
return redirect($this->redirectPath());
default:
return redirect()->back()
->withInput($request->only('email'))
->withErrors(['email' => trans($response)]);
}
}
/**
* Reset the given user's password.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param string $password
* @return void
*/
protected function resetPassword($user, $password)
{
$user->password = bcrypt($password);
$user->save();
Auth::login($user);
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath()
{
if (property_exists($this, 'redirectPath')) {
return $this->redirectPath;
}
return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home';
}
}
One obvious improvement would be to get rid of the redirectPath function and simply use our RedirectsUsers trait, since that is the sole purpose of that trait and the methods are identical.
However the ResetsPasswords trait lives in the vendor/laravel/illuminate/Foundation/Auth directory, which means when we run composer update, the file can be overwritten, which is not what we want.
We could just copy the trait into our AuthTraits folder, give it a new name and then modify it how we want, but is it worth the bother? Probably not. Like I said, we will not be messing with this code much in the future, so it’s probably best just to leave it as is.
So let’s take a brief look at the methods in this trait, keeping in mind that I didn’t write them, so I don’t know every detail, but we can still get a general understanding of how it works.
The first method simply returns the form:
public function getEmail()
{
return view('auth.password');
}
We can tell from the view method that we are expecting an auth folder in the views folder and a password.blade.php file within the auth folder.
When the user gives us his email and submits, we post the form:
public function postEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$response = Password::sendResetLink($request->only('email'), function (Message $message) {
$message->subject($this->getEmailSubject());
});
switch ($response) {
case Password::RESET_LINK_SENT:
return redirect()->back()->with('status', trans($response));
case Password::INVALID_USER:
return redirect()->back()->withErrors(['email' => trans($response)]);
}
}
This one gets fairly complicated when we look under the hood. There is a class named PasswordBroker that lives in vendor/laravel/framework/src/Auth/Passwords/ that has the sendResetLink method.
So the simple explanation is the method tries to find a user with that email and send them a reset link. It switches on the response, and if it is successful it redirects back with status. If not, it returns an error message for invalid user.
Exactly how it’s all stitched together with all the dependencies would make an excellent Laracasts episode. Otherwise it’s beyond the scope of this tutorial.
The next method returns the email subject:
protected function getEmailSubject()
{
return isset($this->subject) ? $this->subject : 'Your Password Reset Link';
}
Next we have the getReset method, which returns the reset view with the token. This will be the form where they type in the new password and confirm it.
public function getReset($token = null)
{
if (is_null($token)) {
throw new NotFoundHttpException;
}
return view('auth.reset')->with('token', $token);
}
Note the use of the default of null for the token. So no need to set up a try/catch, since it will have a value in any case. It’s a nice clean alternative to a try/catch block.
Next we have postReset:
public function postReset(Request $request)
{
$this->validate($request, [
'token' => 'required',
'email' => 'required|email',
'password' => 'required|confirmed',
]);
$credentials = $request->only(
'email', 'password', 'password_confirmation', 'token'
);
$response = Password::reset($credentials, function ($user, $password) {
$this->resetPassword($user, $password);
});
switch ($response) {
case Password::PASSWORD_RESET:
return redirect($this->redirectPath());
default:
return redirect()->back()
->withInput($request->only('email'))
->withErrors(['email' => trans($response)]);
}
}
Ok, so we validate, set the credentials from the form post, use the reset method of the PasswordBroker class, which is accessed via the Password facade.
Then depending on the response, either take them to destination set by redirectPath or go back to the form with errors.
If you are interested, the Password facade class is found at vendor/laravel/framework/src/Illuminate/Support/Facades/Password.php.
There’s a method in there name getFacadeAccessor. It’s probably worth following the chain here for a moment:
protected static function getFacadeAccessor()
{
return 'auth.password';
}
This returns the name of the component. So now we can checkout the PasswordResetServiceProvider class, which you can find at:
vendor/framework/src/Illuminate/Auth/Passwords/PasswordResetServiceProvider.php
Let’s look at one method there:
protected function registerPasswordBroker()
{
$this->app->singleton('auth.password', function ($app) {
// The password token repository is responsible for storing the email addresses
// and password reset tokens. It will be used to verify the tokens are valid
// for the given e-mail addresses. We will resolve an implementation here.
$tokens = $app['auth.password.tokens'];
$users = $app['auth']->driver()->getProvider();
$view = $app['config']['auth.password.email'];
// The password broker uses a token repository to validate tokens and send user
// password e-mails, as well as validating that password reset process as an
// aggregate service of sorts providing a convenient interface for resets.
return new PasswordBroker(
$tokens, $users, $app['mailer'], $view
);
});
}
This is kind of complicated, too much for a beginning tutorial. But the main thing I wanted you to see is that ‘auth.password’ is bound to PasswordBroker. So, since the Password facade is linked to auth.password and auth.password is bound to PasswordBroker, we end up using the methods of PasswordBroker when we call Password.
If you read my service provider tutorial, which was simple, you get some sense of this, and here we have a very advanced example. I should also say it’s extremely well-commented.
If you are a beginner and all this seems like being dropped in the middle of an ocean without a lifejacket or raft, don’t worry, we’ve all been there. Just stay with it, eventually, you will get it.
You don’t have to memorize it all to make progress. Eventually, enough will stick so that you can use what you have as a knowledge base that you can build on. That’s how most of us do it.
Ok, heading back to our ResetsPasswords trait, we need to look at the resetPassword method:
protected function resetPassword($user, $password)
{
$user->password = bcrypt($password);
$user->save();
Auth::login($user);
}
This is the method that sets and saves the new password and logs in the user. We use in our postReset method.
The final method is redirectPath, which we have already covered.
You can see just how involved the process of resetting a password is, and you can see why I didn’t want to build it from scratch or mess with it too much.
The Views
The last step to make our forgot password functionality operational is to create some views. Let’s start by creating the two folders that we will need, which will reside in the views folder. Make an emails folder and an auth folder inside the views folder.
Lets make a password.blade.php file and place it in the emails folder with the following contents:
Click here to reset your password: {{ url('password/reset/'.$token) }}
Things had to get easier sooner or later… You can of course add what ever messaging you want to this view. This is the email that will be sent to the user with the link they need to reset their password.
Next, we need to create reset.blade.php and place it within the auth folder with the following contents:
@extends('layouts.master')
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Reset Password</div>
<div class="panel-body">
@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form class="form-horizontal" role="form" method="POST" action="/password/reset">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="token" value="{{ $token }}">
<div class="form-group">
<label class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" value="{{ old('email') }}">
</div>
</div>
<div class="form-group">
<label class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password">
</div>
</div>
<div class="form-group">
<label class="col-md-4 control-label">Confirm Password</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password_confirmation">
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Reset Password
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
This view is just the form for the password reset, so they can enter the new password.
And finally, we need to create a file named password.blade.php in the auth folder with the following contents:
@extends('layouts.master')
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Reset Password</div>
<div class="panel-body">
@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif
@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form class="form-horizontal" role="form" method="POST" action="/password/email">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="form-group">
<label class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" value="{{ old('email') }}">
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Send Password Reset Link
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
This is another very simple form. Users enter their email and it posts to the postEmail method on the ResetsPasswords trait.
And that is all we need. If you click on the forgot password link in your login form, assuming you followed my How to Make User Login and Registration Laravel 5.1, you will then be taken to the auth.password view to do the lookup on email address and cause the email to be sent.
Then assuming the user receives the email, it will be formatted with the emails.password view, so they can click that link and get them to the reset view, where they can enter the new password.
We don’t cover configuration for sending an actual email in this tutorial, but if you go to app/config/mail.php, there is a setting you can change at the bottom of the file:
'pretend' => true,
When it’s set to true, it will send an email to your application log files, located at app/storage/logs/laravel.log.
You can test your forgot password implementation to the point where it will make an entry in the log file.
That’s gonna do it for this tutorial, I hope you enjoyed. Please comment, share, and like if you can, thanks!
I don’t have a donate button, but If you would like to support my work, you can do so by buying one of my 99¢ books, I really appreciate it.
Great tutorials! I hope many people can benefit from this tutorials. I have a problem with the reset password. It seems the migrations put a foreign key in the password_resets table and I have been looking where is the save statement for this. Hope anyone can help.
LikeLike
Thanks Esteban! I didn’t see a foreign key in the migration for password_resets that comes out of the box with Laravel 5.1, so I’m not sure what you are referring to there.
LikeLike
Nevermind that was a mistake of mine since I used profiles with the build in Laravel functionalities.
Thanks anyway! By the way I ended up creating my own trait so I can personalize the language!
LikeLike
Hi what should i place in my route as get or post to access it? sorry i am just new in using laravel
LikeLike
If you are following this tutorial, you need the following route:
Route::controllers([
‘password’ => ‘Auth\PasswordController’,
]);
With this method of doing it, the type of route is identified on the controller action by the verb, get or post. Please read the full tutorial for details.
LikeLike
im sory bill, can i add you as friend in facebook.. i think i really need your help .. thanks before
LikeLike
Hi Daniel. I can’t offer personalized support. I’m in the end phase of writing a book at the moment. I will be expanding the tutorials on Laravel Tips after I’m done. I hope to get back to it in about 2 weeks.
Laravel has a great forum and community for support. I recommend posting there if you need further assistance.
LikeLike
login ad registration is working fine but if i want to access the password reset form its returning error,,im trying this http://localhost/lv/public/email for pass reset form
LikeLike
the url should be http://localhost/your-project-root/password/email if you are following along the tutorial. Obviously you would replace your-project-root with the name of your actual folder.
LikeLike
i got this
Thanks!!
LikeLike
Hi i am new to laravel i dont understand when i press submit button on sigin form where it goes?
LikeLike
it will go to the route you designate in the action of the form. If you are following the tutorial, that should part be:
action=”/auth/login”
LikeLike
and Routes are
// Authentication routes…
Route::get(‘auth/login’, ‘Auth\AuthController@getLogin’);
Route::post(‘auth/login’, ‘Auth\AuthController@postLogin’);
Route::get(‘auth/logout’, ‘Auth\AuthController@getLogout’);
// Registration routes…
Route::get(‘auth/register’, ‘Auth\AuthController@getRegister’);
Route::post(‘auth/register’, ‘Auth\AuthController@postRegister’);
Route::controllers([
‘password’ => ‘Auth\PasswordController’,
]);
if i change action to the following
action=”auth/login” receive the exception NotFoundHttpException in RouteCollection.php line 161:
LikeLike
HI bill , nice to meet you . I’m the newbie at Laravel 5 .
I have read your tutorial, and just something confuse that i dont know where i should put the Click ## here to reset your password: {{ url(‘password/reset/’.$token) }} ##. Thanks for your tutorial and have a nice day 🙂
LikeLike
nice tutorial
LikeLike
I’m relatively new to Laravel and am trying to figure out the reset password process. It works fine to reset a password, however my home view contains some data that is pulled in from the route when it is navigated to, which is circumvented through the reset process by returning the home view. If I wanted to return to a route instead, how would I do this?
I’ve temporarily got around this by preventing the user from being logged in and the middleware just redirects them to log in, however this isn’t ideal long term.
Ideally, I’d like for their password to be reset, then have them redirected to my HomeController so that the data can be pulled in when the home view is returned, with a flash message to advise that the password has been reset.
Any ideas on how to implement this?
LikeLike
Without digging in too deep, I think you could overwrite the redirectPath method on the PasswordController. The redirectPath method is actually in the ResetsPasswords trait, which the Password controller uses, so you can overwrite this on the PasswordController to specify the route you want in this particular scenario.
By overwriting the method, you don’t have to change anything in the framework code, so you don’t have to worry about losing code when you do composer update. I hope that helps.
LikeLike
how to send google recaptcha there?
I use google recaptcha for me is it best to use own Password Controller?
LikeLike
Sorry, I haven’t worked with Google recaptcha…
LikeLike
Use this: https://packagist.org/packages/noylecorp/laravel-recaptcha
And follow the instructions
LikeLike
Hello Bill
Thank you for detail series for password reset . I have made few changes the in user table`s column email to company_email and now user registration and login works fine but password not working . It is giving
SQLSTATE[42S22]: Column not found: 1054 Unknown column ’email’ in ‘where clause’ (SQL: delete from `password_resets` where `email` is null)
I had changed password_resets email column to company_email but still it is giving the above error
LikeLike
unless they changed something since my install, it should have an email column, so check to see if that is in there.
LikeLike
As i said changed the email to company_email and now i wanted to how we can user custom column in user table like email to cemail and password to cpassword .thank you for your quick response
LikeLike
@bhupendrajadeja you just need to change the field name and the name in the trait (if you made yours or override the password controller). http://stackoverflow.com/questions/27757723/laravel-reset-password-using-different-column-name
LikeLike
now my login working my login fine but it not recognize the password i manually inserted in my database
After pressing submit button it gives me
Login
Whoops! There were some problems with your input.
These credentials do not match our records.
LikeLike
I am little confuse about:( private $redirectTo = ‘/articles’;) why we use this and override.
LikeLike
ok, to answer both of your questions, the password needs to be encrypted, which is why if you enter it manually without encryption, it will not work. Second, the redirectTo property tells it where to go after login and register. If you don’t set that, it defaults to home, but there is no home, so you will get an error.
LikeLike
is that possible to get registration and login on same page using bootstrap models.
LikeLike
after providing email address and clicking on send a reset link button it shows We have e-mailed your password reset link!. But I didn’t get mail. also i have set true as ‘pretend’ => true,
LikeLike
When pretend is set to true, it will not send an email, but it will log to the logs folder in your storage directory. When you set pretend to false, you will also have to configure you email settings in config/email.php, but that is beyond the scope of the tutorial.
LikeLike
Hi my reset email sent to the database but it wont display the password.blade.php in emails folder. link to reset password?
LikeLike
Sorry, can you be more clear in the problem? Is it your routes?
LikeLike
when i press forgot password and sending email successfully i want to go to this path :
Click here to reset your password: {{ url(‘password/reset/’.$token) }} in email folder in views.
but this link is not displaying to me after sending email.
LikeLike
I’m not sure I understand the problem, that link comes to you in the email, it should not redirect there…
LikeLike
Excellent tutorial, I followed this step-by-step and it worked practically first time. Very helpful, many thanks.
LikeLike
Great tutorial!
New using Laravel and when I tried to visit the page for the password reset (“Click here to reset your password”) I’m getting an error saying undefined variable: token. How can I correct this?
LikeLike
Interesting because I think you should get a NotFoundHttpException if the token is not set. So is it possible you have confused the reset page with the password.blade.php view?
LikeLike
Thanks for this tutorial. In my email view, I have something like Click to Reset Password
But when the link is clicked, I instead get http://localhost/password/reset/d46949c7281583b60a15c915622335bce9525994677660bb88d114f60a21bd3a and not the complete URL. The complete URL should have been http://localhost/oap_eneo/public/password/reset/46a19271b866a719b98918ec00c4b0e178fddc7e29bb08eb127d0e0674de57f6. What could be the problem (oap_eneo/public is missing)
LikeLike
looks like it’s a routing issue. You should not have to have /public in the url.
LikeLike
Thank you so much for writing this! I’m working on my first Laravel app and took the same approach of not using the auth or registering controllers from the framework. But I didn’t have any idea about how to manage the password reset part, at least until now!
LikeLike
You are welcome…
LikeLike
Hi
When i click Send Password Reset Link it shows an error: Class auth.password.tokens does not exist
LikeLike
You typically get an error like that when you are missing a use statement.
LikeLike
Hi,
i am getting “NotFoundHttpException in RouteCollection.php line 161:” when clicking any link(Login,Register,forgot password)
LikeLike
When you get that error, it means you do not have a matching route for those links.
LikeLike
Thank you for your responce,after fix that issue, when try to make registration it shows error PDOException in Connector.php line 55:
SQLSTATE[HY000] [1045] Access denied for user ‘homestead’@’localhost’ (using password: YES)
LikeLike
The error message is fairly explicit. You have a problem connecting to your database, so check to see if you have your .env file set correctly.
LikeLiked by 1 person
I am laravel beginner,how to find the .env file?
LikeLike
In my laravel project not displaying .env file
LikeLike
It should be visible in your IDE, it’s not in that app folder, it should be in your root folder, along with your composer file, etc.
LikeLike