Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

rootuser's avatar

Jetstream reset password issue

Hello,

I have a question regarding a worrying detail of the password reset function of jetstream. Whe you use this function, an email is sent to the given account. This email contains a link containing a token and the email address. When you follow the link, you get a web form with the fields email, password and confirm password, where the email field is already filled in. If you fill in the form and submit it, it is validated, and if you should have changed the email address, you get an error message like 'token does not match'.

What worries me is the fact that, imho, it is completely pointless both to send the email in the link and to ask for it in the cancellation form. The token would be completely sufficient to identify the applicant.

Can anyone tell me why the function is implemented in this way? Am I missing something?

Thanks!

Kind regards, Tom

0 likes
8 replies
LaryAI's avatar
Level 58

Hello Tom,

The reason why the email address is included in the password reset link and why it is pre-filled in the password reset form is primarily for user experience and security reasons.

  1. User Experience: Pre-filling the email field in the form is a convenience for the user. It saves them from having to remember and type in their email address again, which they may have just entered when requesting the password reset. This is especially helpful if the user has multiple email accounts and might not remember which one is associated with their account on your site.

  2. Security: Including the email in the token verification process adds an extra layer of security. The token is generated in a way that is specific to the user's email address. By requiring the token and the email address to match, you ensure that the password reset request is legitimate and that the token hasn't been intercepted and used in an attempt to reset the password of a different user's account.

Here's a simplified example of how the password reset might be implemented in Laravel Jetstream:

// When generating the reset link
$token = Str::random(64);
PasswordReset::create([
    'email' => $request->email,
    'token' => Hash::make($token),
    // ... other fields like created_at
]);

$url = route('password.reset', ['token' => $token, 'email' => urlencode($user->email)]);

// Send the email with $url to the user's email address

And when handling the password reset form submission:

public function reset(Request $request)
{
    $request->validate([
        'token' => 'required',
        'email' => 'required|email',
        'password' => 'required|confirmed|min:8',
    ]);

    $passwordReset = PasswordReset::where('email', $request->email)->first();

    if (!$passwordReset || !Hash::check($request->token, $passwordReset->token)) {
        return back()->withErrors(['email' => 'The provided token does not match your email address.']);
    }

    // Reset the user's password
    $user = User::where('email', $request->email)->first();
    $user->password = Hash::make($request->password);
    $user->save();

    // Delete the password reset token
    $passwordReset->delete();

    // Log the user in and redirect them to the home page
    Auth::login($user);

    return redirect()->route('home');
}

In this example, the token is hashed before being stored in the database for security reasons. When the user submits the password reset form, the application checks that the hashed token in the database matches the token provided in the form, and that the email address is the same as the one the token was created for.

This implementation ensures that the password reset process is secure and user-friendly. If you have any further questions or concerns, feel free to ask!

Kind regards, LaracastsGPT

rootuser's avatar

Just a few notes regarding the AI reply: argument number 2 (enhanced security) is obviously absurd. It could indeed be used as a security feature if it wasn't sent in the email and automatically filled out in the form. But not in its current form. This is exactly the fact that is concerning me and why I am asking this question.

So thanks, Lary, but unfortunately this does not help.

Kind regards, Tom

puklipo's avatar

Password reset is used by guest users. Receiveable email and temporary token are required. If it were possible to reset by email only, anyone could reset it. http://localhost/reset-password?email=admin@localhost

https://github.com/laravel/fortify/blob/29cdcaf0afc12d5b940c0dd73da2d92df4f7d2ad/routes/routes.php#L47

->middleware(['guest:'.config('fortify.guard')])
->name('password.reset');

Let's check out general password reset features that aren't in Laravel.

rootuser's avatar

@puklipo Thanks for your reply, but I think you've got me wrong. Yes, you need receiveable email and temporary token. But not at the same time!

Password reset is triggered by a guest user. At this point, you have to give an email address. Jetstream does only need the email to

  • find if that user exists
  • if it extsts, create a token and send a reset email
  • no matter if the user (email) exists in the app database or not: the app should tell the guest user: a reset email is sent to your address.

As of now, the reset links in reset emails contain token AND email address. But that is not necessary, because in the database there is already a dataset containing email address and token. So it would be sufficient to make the request with only the token and then only input the password in the reset form. Jetstream could then find the email address by a simple database query and update the according user account. If you wished to make it even more secure, you might ask for the email address in the reset form and check if it is also correct and suiting the token. Currently, however, the email is also sent in the link and automatically inserted into the reset form. This is no gain of security.

But either way: the email address does not have to be included in the reset link. On the contrary: this provides a potential attacker with data that he may be able to use against the user.

There are a lot of different implementations of password resets out there, so it is difficult to decide what is best or most secure. I'm not an expert in this respect either, so I'm asking why Jetstream is doing it this way.

Kind regards, Tom

Snapey's avatar

@rootuser

Thanks for your reply, but I think you've got me wrong. Yes, you need receivable email and temporary token. But not at the same time!

How on earth does this convenience change the level of security? The person's email is there in plain text in the To: field. This saves the user having to enter it on the password reset form and means that the token can be checked in combination with the email address. It needs this to find the right token to check since it cannot just text search for the token in the password reset table.

there is already a dataset containing email address and token

no because the token sent is hashed and not sitting as a direct copy of the token

rootuser's avatar

@Snapey

Thanks for your reply. Look, I am just trying to learn why it is implemented that way. If I am clumsy and have upset anyone, I am sorry.

I do not claim that it is a security risk to put the email address in the link. I just do not understand the advantage of hashing the token, for it is sent to the user in cleartext anyway. What kind of possible attack can be prevented that way? The implementation is as it is, so I suspect there is a very good reason for that. I am obviosly missing something, and I am asking you to help me out.

Thanky for your patience!

Kind regards, Tom

Snapey's avatar
Snapey
Best Answer
Level 122

@rootuser say, for instance, someone gets hold of a backup of the database (because its stored on another server somewhere or a dev's computer or in a compromised cloud account.

If the token was in plaintext, I could look in the password resets table, get a token and the user's email and reset that user's password.

Having hashed the token means that is not possible. But it is also now not possible to just search for the token to know who's password is being reset, so the user has to provide a hint with their email.

rootuser's avatar

@Snapey

Thank you for you reply.

I suspected that this could be the reason. But to be honest, I felt the potential danger was too small for this measure. It is also the case that the reset code is only valid for one hour, so an attacker would have to be very quick. I would therefore probably not have implemented it myself. Nevertheless, it is of course better to eliminate even small dangers.

So thanks again for the explanation!

Kind regards, Tom

Please or to participate in this conversation.