How Do You Send A First Time User A Password Reset Link? (

Published 3 months ago by larel_b

I followed @bashy on this post: https://laracasts.com/discuss/channels/laravel/reset-password-manually-without-email

I am generating a token like this:

$reset_token = hash_hmac('sha256', Str::random(40), $user);

I am inserting a record in the password_resets table like this:

    DB::table('password_resets')->insert([
       'email' => $request->email,
        'token' => $reset_token,
        'created_at' => Carbon::now(),
    ]);

The user gets an e-mail sent through a custom notification with the correct token (it matches on the link and in the hidden input field on the view).

I keep getting this after following the link, filling in the form, and clicking on reset password:

This password reset token is invalid.

I have also tried this to generate the token (same error):

$reset_token = strtolower(str_random(64));

Best Answer (As Selected By larel_b)
Snapey

Not sure using notifications.

    protected function registered(Request $request, $user, $reset_token) {
        $user->notify(new UserRegisteredNotification($user, $reset_token));  // no need to pass user to the notification
    }

Notification

public $reset_token;

public function __construct($reset_token)
{
    $this->reset_token = $reset_token;
}
public function toMail($notifiable) {
        $pw_reset = DB::table('password_resets')->where('email', $this->user->email)->first();
        return (new MailMessage)
            ...
            ->action('Reset Password', url(config('app.url').route('password.reset', $this->reset_token, false)))  // get the token from this object
            ...
    }
Snapey
Snapey
3 months ago (865,465 XP)

Why not just use a random string. This way you are not getting any unsafe characters in the token.

There should be no good reason to use the user in the token.

$reset_token = str_random(40);
larel_b

@Snapey Thanks for the fast reply. I tried that as I mentioned at the bottom of the post.

Snapey
Snapey
3 months ago (865,465 XP)

And you are sending the same token to the user? Have you verified it by looking in your password_reset table and compare it to the url in the email.

larel_b

@Snapey Yep, it is exactly the same and the same in the hidden input field of the view.

Snapey
Snapey
3 months ago (865,465 XP)

Well it only does a where statement on the reset table.

Try that?

select * from password_resets where email='[email protected]' and token='token here'

larel_b

@Snapey The select query works just fine and it finds the entry used in the link.

Snapey
Snapey
3 months ago (865,465 XP)

Ok so sorry, I stand corrected.

The password reset does contain a hashed token. It is created with

   public function createNewToken()
    {
        return hash_hmac('sha256', Str::random(40), $this->hashKey);
    }

but I'm having trouble understanding where $this->hashkey comes from. The original code from @bashy says $user but is that an object or id?

Snapey
Snapey
3 months ago (865,465 XP)

Not only that but the token created above seems to be hashed again;

protected function getPayload($email, $token)
{
    return ['email' => $email, 'token' => $this->hasher->make($token), 'created_at' => new Carbon];
}
larel_b

Yeah, I traced it back to the PasswordBrokerManager in vendor/laravel/framework/src/Illuminate/Auth/Passwords.

Look in the createTokenRepository method.

Snapey
Snapey
3 months ago (865,465 XP)

Just typed loads of code and got blocked by the Cloudflare again. Pissed off this keeps happening...

larel_b

I'm really sorry that happend to you.

Snapey
Snapey
3 months ago (865,465 XP)

Had to resort to Pastebin

https://pastebin.com/dbLJCycR

This is not a very good solution though since if the password hashing changes or the way the framework creates the token changes then your code will break

eg https://laravel-news.com/laravel-agron2i

If you are going to create your own token perhaps you should check it and reset the user password yourself also.

larel_b

Thanks for that! What is the context of this in $this->app?

Snapey
Snapey
3 months ago (865,465 XP)

Its getting the key from the config (which in turn comes from the .env)

so the same as $key = config('app.key');

larel_b

Please sign in or create an account to participate in this conversation.