Framework: Password reset e-mail missing e-mail in URL

Created on 3 Oct 2016  Â·  64Comments  Â·  Source: laravel/framework

  • Laravel Version: 5.3.15
  • PHP Version: 7.0.0
  • Database Driver & Version: Oracle

    Description:

When sending the password reset e-mail in Laravel 5.3, the reset link have a token, but doesn't have the user e-mail. This way, the reset form will not load the user e-mail. In 5.2, the reset link had the user e-mail.

Without the user e-mail in the URL, Illuminate\Foundation\Auth\ResetsPasswords.php will send a null e-mail in showResetForm, because $request->email will evaluate to null. Then, the reset form provided by the framework from Illuminate\Auth\Console\stubs\make\views\auth\passwords\email.stub will show a blank e-mail in the following input:

<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required>

Digging up who missed sending the e-mail in the URL, we can found a Illuminate\Auth\Notifications\ResetPassword.php which does the following action:

->action('Reset Password', url('password/reset', $this->token))

As you can see, by default Laravel does not add the e-mail in the request URL. Since this trait usually is overwritten, we can add the e-mail to the URL to make things working again. The issue here is that, by default, Laravel is missing this e-mail in the request url.

Steps To Reproduce:

Send a reset e-mail link and open the link provided in the e-mail.

Most helpful comment

Solution

Here's how to do it without altering vendor/core - tested with Laravel 5.6 (should be fine with later versions, too):

1. Create own notification

Firstly create a new notification for your app:

php artisan make:notification ResetPassword

Then open the newly created file: app\Notifications\ResetPassword.php and make the following changes:

Add public $token; to the beginning of the class (ie. after use Queueable).

Add $this->token = $token; to the body of your __construct() method.

Add $token as parameter to the __construct() method (so it reads __construct($token)).

Add the following to the body of your toMail() method (replacing what's there by default):

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

What you've done is create a copy of the original vendor/core version of the ResetPassword notification (found in /vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php). This allows you to make customisations to it without changing the core Laravel files.

(One difference we've made is adding $notifiable->email to the body of the email.)

2. Point to your notification

Now we need to tell Laravel to call our notification instead of the one found in the CanResetPassword trait. To do this, just edit your App/User.php model:

Add use App\Notifications\ResetPassword; to the top of the file (ie. a link to the notification you just created) and then add a new method:

    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPassword($token));
    }

3. Update your routes

Finally we need to update our routes to include the new {email} parameter:

Route::get('/password/reset/{token}/{email}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');

Now if the user fills in the /password/reset form, they will be sent your notification, with the additional email parameter.

This now works perfectly!

Additional: Handle errors

You can improve on the above by adding a bit of error handling. To do this, just pass the email to your form, to handle things if there's a problem:

Add the following method to Auth/ForgotPasswordController.php:

    protected function sendResetLinkFailedResponse(Request $request, $response)
    {
        return back()->withErrors(
            ['email' => trans($response)]
        )->withInput($request->only('email'));
    }

And then place the following at the top: use Illuminate\Http\Request;.

And you're done!

Additional: Finesse the UX

If you want, I'd also recommend modifying the semantic HTML in your views (regardless of your design). Go to resources/views/auth/passwords/reset.blade.php and remove the autofocus attribute from the email input element, and add readonly. (I move the autofocus to the first password input element myself.)

Additional: Encrypt the email address in the URL

You can still tidy things up a bit more by encrypting the email in the URL. This isn't for security, it's to make it look prettier (so to speak) and make it less likely for the user to mess with the URL and break something.

To do this, just go to the notification we created in step 1 and change $notifiable->email to encrypt($notifiable->email).

Then go to app/Http/Controllers/Auth/ResetPasswordController.php and add the following method:

    public function showResetForm(Request $request, $token = null)
    {
        return view('auth.passwords.reset')->with(
            ['token' => $token, 'email' => decrypt($request->email)]
        );
    }

Don't forget to place use Illuminate\Http\Request; at the top, too.

And you're done! (Again.)


I've tested all this fully, and it works perfectly, but if you have an issue, let me know.

Good luck!

All 64 comments

This may have been on purpose. Not completely sure, though.

In 5.3 you get asked for the email while providing your new password.

Which does not make sense. If I open a reset url, the form should be opened with the user's e-mail.

Otherwise, why there are these pieces of codes in the framework:

Illuminate\Foundation\Auth\ResetsPasswords.php

public function showResetForm(Request $request, $token = null)
{
    return view('auth.passwords.reset')->with(
        ['token' => $token, 'email' => $request->email]
    );
}

Illuminate\Auth\Console\stubs\make\views\auth\passwords\reset.stub

<input id="email" type="email" class="form-control" name="email" value="{{ $email or old('email') }}" required autofocus>

If the desired behavior in 5.3 is to force the user to input it's e-mail again, why the showResetForm and the view are trying to send and display it?

Thank you @chekovsky, I have also just stumbled upon it.
As I can guess $request->email was left from older 5.2 version.
Question is why the reset password link/functionality have changed and why user has to type in the email address again (form is used elsewhere?).

Hi there!

Those lines are leftovers from 5.2, and they won't fix it in 5.3.

In 5.3 the best solution is to override it and show your own form with your own data, like the user's e-mail.

Best regards,
Rafael Pacheco.

Hi there,
Do we have update on this guys? (the mail field still remains in L5.4)

Same issue here in Laravel 5.4

@khalist It's been removed since 5.2. If it bothers you so much simply remove $email from the view file.

@kjdion84 does the form actually work if you do that?

Yes.

@kjdion84 It doesn't seem to work if you are logging in the user with an email (or with a username, in fact). @retrieveByCredentials from the UserProvider interface cannot locate the user without an email

@kjdion84 Your solution won't work, because the validator raises error for missing email field. In other hand there is no email field in the view and you want to delete nothing!

@zmonteca I solved the problem by adding hidden email field to view by locating the token in password_resets table.

@khalilst how are you locating the token since its now hashed in 5.4?

@Vrutin My mistake. Actually I forgot how to solve this problem before. Email souldn't be a hidden field, it must be a simple input field. In fact I asked the user to enter his/her email in password reset form.

@khalilst No worries, I have that currently but it kind of doesn't make sense from the users' point of view that they get a password reset link but then having to enter email again.

@Vrutin I know this is an UX issue and of course resolving a bug by adding another smaller bug.
The best way to solve this problem, can be done by our dear friend @taylorotwell by removing required email validation from core controllers and maybe by removing token hashing.
I hope this have been done in 5.5

@khalilst, well the process has been kept same in Laravel 5.5 too. We still need to do this in a custom way

Agreed. It's terrible as is.

Did someone solve this problem? I am currently using laravel 5.4 with same issue.

What's your particular issue? There have been several mentioned here.

return view('auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);

The email here returns empty

@khalilst @Mayonado @zmonteca

I have fixed it by editing/hacking ResetPassword.php notification & reset.blade.php file. Now this does mean you are editing vendor file, so it's your choice to do this or not.

ResetPassword.php

The file can be found at (5.4):

../vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php

Amend the toMail method:

From:

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', url(config('app.url').route('password.reset', $this->token, false)))
            ->line('If you did not request a password reset, no further action is required.');
    }

To

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', url(config('app.url') . route('password.reset', [$this->token, 'email=' . $notifiable->email], false)))
            ->line('If you did not request a password reset, no further action is required.');
    }

Notice the updated parameters argument, $notifiable resolves to User object hence you can access the email property:

[$this->token, 'email=' . $notifiable->email]

reset.blade.php

The file can be found at (5.4):

../resources/views/auth/passwords/reset.blade.php

The HTML may slightly differ here based on your CSS framework. In addition, you may want to show email field as read-only or make it hidden input. I prefer the user seeing the email they requested the password reset for.

From

<input type="text" id="email" name="email" class="input {{ $errors->has('email') ? ' is-danger' : '' }}" value="{{ old('email') }}">

To

<input type="text" id="email" name="email" class="input {{ $errors->has('email') ? ' is-danger' : '' }}" value="{{ $email }}" readonly>

Notice the updated value attribute, added the readonly attribute to stop user amending the email.

... value="{{ $email }}" readonly>

Hope this helps others.

@Vrutin your work around this issue is appreciated. In your solution, you have added email in the password reset link (being shared in email)

This appears to be a bad way to handle the functionality as displaying email in the URL is not a suggested and adopted practice.

@ashwani23 What if we encrypt the email and then decrypt back. Would that be okay?

@Vrutin how does your route look like? What I did was to change the route to:
reset/{token}/{email}

And changed toMail function body to include this:
->action('Reset Password', url('reset', [$this->token, $notifiable->email]))

Cheers,

@iranianpep I didn't change the route, as I am passing it as a query parameter and the default implementation uses $request->email.

Though your implementation would work well too 👍

I ended up encrypting the email using the encrypt() method and then used the decrypt() method to get back users email back.

@Vrutin because for some reason when I used your approach the reset link was like
/reset/THE_GENERATED_TOKEN/[email protected]

and since it didn't find any route I got page not found error. Anyway, I'll use my own solution. But thanks for the encryption suggestion.

Another simple approach:

  1. Store email in session or even simpler in cookie or LocalStorage.
  2. Reload it from previous storage to reset form.
  3. Delete the email key from the storage.

Majority of users reset their passwords instantly after requesting.
Others should enter their emails again because they are very little percentage ;)

I don't think storing email in the session is a good idea because it is mainly for temporary sensitive data. Also if a website has got loads of visitors you don't wanna use the session for storing emails.

@iranianpep Session is for temporary sensitive data?! Did u use flash in laravel? It stores simple messages in Session. BTW read this and consider item 3.

However as I said before, this problem would be solved from here
Illuminate\Foundation\Auth\ResetsPasswords by @taylorotwell, @JeffreyWay , but apparently they are too busy

As I said, it is mainly for temporary sensitive data e.g. logged in user otherwise you're free to overwhelm it with a bunch of emails! Also wikipedia is not recommended as a solid programming reference.

@iranianpep So you didn't use flash. Or you're using session mainly in a wrong way.
And of course my approach is not for bunch of emails. (Don't play with words and don't go off topic)

Simply don't use mine if you don't like it.

Think he's confusing session for cache. There wouldn't be a "bunch" of emails.

Well this is going nowhere! This is the last that I'm trying to justify myself: Yes, I have used flash messages. As I said you can store anything on the session however because it's stored on the server it is (again!) mainly for storing sensitive data or in another word it's the best option if you need for example store authenticated user, shopping cart details, etc.

@devcircus: in a high traffic website you never know how many users are requested to reset their passwords and the size of session files can grow quickly.

@iranianpep +1 for your last off topic

@Vrutin I use your solution, and works great!! I use encrypt() and decrypt() to hide the values.

Hi guys, I've just realize this issue since I was working with 5.2. I love @Vrutin answer and found a better way rather than editing vendor.

Since the notification email is sent from CanResetPassword trait, and our user model extends Illuminate\Foundation\Auth\User which use the trait. We can just override sendPasswordResetNotification() in our user model and create our own notification class like so:

public function sendPasswordResetNotification($token)
{
    $this->notify(new App\Notifications\ResetPasswordNotification($token));
}

Hope it helps. :)

That's why I've never really understood this issue. Laravel doesn't use it and these traits are designed to be overwritten.

From User point of view:

  1. Since the system already know my email, why I need to retype again?
    From Developer point of view:
    Email is sensitive data??

From my point of view, I think it will be great if it is auto fill, I know Laravel Open Source, you can do everything you want to do it.
When you propose and show the password reset flow, I am sure your client will ask you why I need to retype again the password since the system know it. My answer will be for security purpose. Maybe we need more clear on this move?

Solution

Here's how to do it without altering vendor/core - tested with Laravel 5.6 (should be fine with later versions, too):

1. Create own notification

Firstly create a new notification for your app:

php artisan make:notification ResetPassword

Then open the newly created file: app\Notifications\ResetPassword.php and make the following changes:

Add public $token; to the beginning of the class (ie. after use Queueable).

Add $this->token = $token; to the body of your __construct() method.

Add $token as parameter to the __construct() method (so it reads __construct($token)).

Add the following to the body of your toMail() method (replacing what's there by default):

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

What you've done is create a copy of the original vendor/core version of the ResetPassword notification (found in /vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php). This allows you to make customisations to it without changing the core Laravel files.

(One difference we've made is adding $notifiable->email to the body of the email.)

2. Point to your notification

Now we need to tell Laravel to call our notification instead of the one found in the CanResetPassword trait. To do this, just edit your App/User.php model:

Add use App\Notifications\ResetPassword; to the top of the file (ie. a link to the notification you just created) and then add a new method:

    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPassword($token));
    }

3. Update your routes

Finally we need to update our routes to include the new {email} parameter:

Route::get('/password/reset/{token}/{email}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');

Now if the user fills in the /password/reset form, they will be sent your notification, with the additional email parameter.

This now works perfectly!

Additional: Handle errors

You can improve on the above by adding a bit of error handling. To do this, just pass the email to your form, to handle things if there's a problem:

Add the following method to Auth/ForgotPasswordController.php:

    protected function sendResetLinkFailedResponse(Request $request, $response)
    {
        return back()->withErrors(
            ['email' => trans($response)]
        )->withInput($request->only('email'));
    }

And then place the following at the top: use Illuminate\Http\Request;.

And you're done!

Additional: Finesse the UX

If you want, I'd also recommend modifying the semantic HTML in your views (regardless of your design). Go to resources/views/auth/passwords/reset.blade.php and remove the autofocus attribute from the email input element, and add readonly. (I move the autofocus to the first password input element myself.)

Additional: Encrypt the email address in the URL

You can still tidy things up a bit more by encrypting the email in the URL. This isn't for security, it's to make it look prettier (so to speak) and make it less likely for the user to mess with the URL and break something.

To do this, just go to the notification we created in step 1 and change $notifiable->email to encrypt($notifiable->email).

Then go to app/Http/Controllers/Auth/ResetPasswordController.php and add the following method:

    public function showResetForm(Request $request, $token = null)
    {
        return view('auth.passwords.reset')->with(
            ['token' => $token, 'email' => decrypt($request->email)]
        );
    }

Don't forget to place use Illuminate\Http\Request; at the top, too.

And you're done! (Again.)


I've tested all this fully, and it works perfectly, but if you have an issue, let me know.

Good luck!

or you want to overide route reset

in ResetPassword notifiation:
use URL;

$link = URL::to("password-reset?token={$this->token}&email={$notifiable->email}");

return (new MailMessage)
                    ->line('You are receiving this email because we received a password reset request for your account.')
                    ->line('Click the button below to reset your password:')
                    ->action('Reset Password', $link)
                    ->line('If you did not request a password reset, no further action is required.');

and then you create route
Route::get('password-reset', YourController);
acces on your controller
$request->email or $request->token

I had this working in a project but I made a lot of changes to the project but I never touched this. I wanted to test if everything works out but got the surprise of my life that it didn't.

Sending the reset link response was part one, resetting the passwords was part two.

If you are a newbie like me, you won't be getting the token and e-mail field in your post request if you do not add it as parameters in your route and method. I ended up doing this

Route::post('/password/reset/{token}/{email}', 'ResetPasswordController@reset')->name('password.reset');
public function reset(Request $request, $token, $email)
    {
        $request->request->add(
            [
                'token' => $token,
                'email' => $email,
            ]
        );
        $this->validate($request, $this->rules(), $this->validationErrorMessages());
        $response = $this->broker()->reset(
            $this->credentials($request), function ($user, $password) {
                $this->resetPassword($user, $password);
            }
        );

        return $response == Password::PASSWORD_RESET
                    ? $this->sendResetResponse($response)
                    : $this->sendResetFailedResponse($request, $response);
    }

For the security guys, I'll think about security later; let this work first!!! If you think this could be better, kindly give suggestions

Am I missing something big here? If the email is being stored in the database alongside the token, can it not just be retrieved and auto-populated into the form? Storing it in the URL seems silly, you have to check the token when you follow the email, right?

The token is already supposed to be a secure identifier, if the user has the token they surely know the email. So why can't we just look it up from the database?

@ryanrapini, Yes, ur missing a point. The token hashed in url! So u need email to find the token itself in db (and not vice versa)

Password reset flow works perfectly since this change. I still haven't understood the issue. Plus it's simple to add the old functionality back if you want.🤔

here's a hack for finding the email with token only

public function showResetForm(Request $request, $token = null)
    {
        $dbTokens = DB::table('password_resets')->get();
        $email = '';
        foreach($dbTokens as $dbToken) {
            if($this->hasher->check($token, $dbToken->token)) {
                $email = $dbToken->email;
                break;
            }
        }

        return view('auth.passwords.reset')->with(
            ['token' => $token, 'email' => $email]
        );
    }

don't know about the performance though..

i am using 5.6 and this problem still continues. default e-mail provided by laravel does not include an email field in its URL. however it wants an email to reset the password. in the documentation it shows that email field should be filled by old method. like that => value="{{ old( 'email' ) }}" . however it does not work for me.

now, they used old() method in documentation already says us they never expect mail form url. They want to get it from a session which makes sense but why its not working?

on the other hand if they expect it from a session why they have this code in Illuminate\Foundation\Auth\ResetsPasswords.php

public function showResetForm(Request $request, $token = null)
{
    return view('auth.passwords.reset')->with(
        ['token' => $token, 'email' => $request->email]
    );
}

i am confused.

That line just wasn't removed when the implementation changed. If you decide to pass an email, the helper may come in handy. Passing the email is completely unnecessary though. Honestly that line should just be removed.

Passing the email is completely unnecessary though

so why my controller ResetPasswordController@reset shows "email field is required" ?

It's definitely required to type the email into the form. If my memory is correct, the logic changed a while back so as to not send the email in the url, for security reasons. The 'old' method was just never removed from the view. However, if you wish to send the email in the url, you are free to change the reset functionality as you see fit.

In terms of UX it seems quite annoying to ask the user to type their password in again, but it's undoubtedly less secure. I guess it depends on how important security is in your app.

Side note: I've updated my answer above. I had previously missed out some minor steps (which could be deduced, but I've explicitly mentioned them now), and I've successfully tested it with a fresh installation of Laravel 5.6.

@khalilst @Mayonado @zmonteca

I have fixed it by editing/hacking ResetPassword.php notification & reset.blade.php file. Now this does mean you are editing vendor file, so it's your choice to do this or not.

ResetPassword.php

The file can be found at (5.4):

../vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php

Amend the toMail method:

From:

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', url(config('app.url').route('password.reset', $this->token, false)))
            ->line('If you did not request a password reset, no further action is required.');
    }

To

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', url(config('app.url') . route('password.reset', [$this->token, 'email=' . $notifiable->email], false)))
            ->line('If you did not request a password reset, no further action is required.');
    }

Notice the updated _parameters_ argument, $notifiable resolves to User object hence you can access the email property:

[$this->token, 'email=' . $notifiable->email]

reset.blade.php

The file can be found at (5.4):

../resources/views/auth/passwords/reset.blade.php

The HTML may slightly differ here based on your CSS framework. In addition, you may want to show email field as read-only or make it hidden input. I prefer the user seeing the email they requested the password reset for.

From

<input type="text" id="email" name="email" class="input {{ $errors->has('email') ? ' is-danger' : '' }}" value="{{ old('email') }}">

To

<input type="text" id="email" name="email" class="input {{ $errors->has('email') ? ' is-danger' : '' }}" value="{{ $email }}" readonly>

Notice the updated value attribute, added the readonly attribute to stop user amending the email.

... value="{{ $email }}" readonly>

Hope this helps others.

Now, you don't need to override the vendor file of Notification. One way to override the toMail method is creating a service provider like this:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
use Illuminate\Support\Facades\Lang;
use Illuminate\Notifications\Messages\MailMessage;

class NotificationServiceProvider extends ServiceProvider
{
    public function boot()
    {
        ResetPasswordNotification::toMailUsing(function ($notifiable, $token) {
            return (new MailMessage)
                   ->subject(Lang::getFromJson('Reset Password Notification'))
                   ->line(Lang::getFromJson('You are receiving this email because we received a password reset request for your account.'))
                   ->action(Lang::getFromJson('Reset Password'), url(config('app.url').route('web.password.reset', [$token, 'email=' . $notifiable->email], false)))
                   ->line(Lang::getFromJson('If you did not request a password reset, no further action is required.'));
        });
    }
}

Then, registry your service provider in config/app.php:

<?php

return [
  # ...
  'providers' => [
    # ...
    App\Providers\NotificationServiceProvider::class
  ]
];

These work arounds are great, but it is a huge waste of everyone's cumulative time to have to implement a workaround for a framework.

Check out what the reset password form looks like for Hulu.com, notice they do not require you to type in your email address. I looked at the logic for Laravels PW reset and it seems we cannot omit the email address from the form. If you could check the hash in the DB it would work, but MySQL does not support bcrypt. Does this mean that hulu us hashing their tokens using a MySQL supported hash algorythm such as SHA?

Is there an issue with storing the plain text token in the DB?

screen shot 2018-10-23 at 9 27 37 pm

The solution I posted here solves this problem (at least from a UX perspective) by hashing the user’s email and hiding it in the URL.

@khalilst @Mayonado @zmonteca

I have fixed it by editing/hacking ResetPassword.php notification & reset.blade.php file. Now this does mean you are editing vendor file, so it's your choice to do this or not.

ResetPassword.php

The file can be found at (5.4):

../vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php

Amend the toMail method:

From:

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', url(config('app.url').route('password.reset', $this->token, false)))
            ->line('If you did not request a password reset, no further action is required.');
    }

To

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', url(config('app.url') . route('password.reset', [$this->token, 'email=' . $notifiable->email], false)))
            ->line('If you did not request a password reset, no further action is required.');
    }

Notice the updated _parameters_ argument, $notifiable resolves to User object hence you can access the email property:

[$this->token, 'email=' . $notifiable->email]

reset.blade.php

The file can be found at (5.4):

../resources/views/auth/passwords/reset.blade.php

The HTML may slightly differ here based on your CSS framework. In addition, you may want to show email field as read-only or make it hidden input. I prefer the user seeing the email they requested the password reset for.

From

<input type="text" id="email" name="email" class="input {{ $errors->has('email') ? ' is-danger' : '' }}" value="{{ old('email') }}">

To

<input type="text" id="email" name="email" class="input {{ $errors->has('email') ? ' is-danger' : '' }}" value="{{ $email }}" readonly>

Notice the updated value attribute, added the readonly attribute to stop user amending the email.

... value="{{ $email }}" readonly>

Hope this helps others.

You're a genius. I was still facing this issue with laravel 5.6

Just add the following code in Model User.php

 /**
 * Send the password reset notification.
 *
 * @param  string  $token
 * @return void
 */
public function sendPasswordResetNotification($token)
{
    $this->notify(new ResetPasswordNotification($token.'/'.$this->email));
}

I faced this issue with laravel 5.7

The fix was very simple, update file resources\views\auth\passwords\reset.blade.php:

change the line from:
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ $email or old('email') }}" required autofocus>

to

<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ $email ?? old('email') }}" required autofocus>

notice the difference is the little 'or' to '??'

I faced this issue with laravel 5.7

The fix was very simple, update file resources\views\auth\passwords\reset.blade.php:

change the line from:
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ $email or old('email') }}" required autofocus>

to

<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ $email ?? old('email') }}" required autofocus>

notice the difference is the little 'or' to '??'

It's not working

It's not working

Just follow the my guide above. It's simple and it works without having to alter your vendor folder (which you really shouldn't do).

Add use App\Notifications\ResetPassword; to the top of the file (ie. a link to the notification you just created)

Thanks for this great and proper solution that works for me in 5.8.

Just a note : personnaly i needed to call
use App\Notifications\ResetPassword as ResetPasswordNotification;
instead to properly call the new notification instead of the vendor one.

I may be mistaken, but asking for an e-mail address in the password change form itself allows the form to be brute-forced (even if it's throttled). Basically, if someone guesses an e-mail address, will be able to get access to the site.

Asking for an e-mail address in the form defeats the purpose of the token.

In the current state of things, the token is useless (it only tells the router to load a different controller method if it is present).

I went on a solution where I overrode the trait in the controller itself, and retrieved the e-mail address with a token. This comes with a slight performance impact, as it needs to go through the password reset table to match the token, but on the site it's going to be used, it won't matter.

It doesn't defeat the purpose. Just another level, maybe unnecessary, of security. The email must match the email that initiated the reset an the token must match the token stored against the email address. Brute force isn't possible in this case.

There’s a token which needs to match the email address. Having an email address isn’t enough to change someone’s password.

On 3 May 2019, at 12:16, Ervin Nagy notifications@github.com wrote:

I may be mistaken, but asking for an e-mail address in the password change form itself allows the form to be brute-forced (even if it's throttled). Basically, if someone guesses an e-mail address, will be able to get access to the site.

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

For everyone who doesn't want to touch vendor or create a lot of extra code, there's a small simple dirty JS solution:

var url = window.location.href;
if (url.indexOf("/password/reset/") >= 0) {
    var email = url.substr(url.indexOf('?email=') + 7);
    $("#email").val(decodeURIComponent(email));
}

Checks if you're on the password reset page and copies the email address from the URL in the email address box.

Was this page helpful?
0 / 5 - 0 ratings