I haven't found a solution for this problem yet: failing to send password reset emails while email column in database is encrypted (Laravel 8.57.0 with JetStream, Fortify, Livewire). I have browsed some other Q&A but they are mostly for Laravel 5 and I'm afraid they are not fully compatible.
Here we go:
Some columns in Users table are encrypted using betterapp/laravel-db-encrypter (https://github.com/betterapp/laravel-db-encrypter) - including email column. There's a separate username field for login (for safety reasons mostly).
Fortify routes are disabled and I maintain them on my routes/web.php.
config/auth.php file has
'username' => 'username',
'email' => 'email'
Reset password view leads to PasswordResetLinkController in Laravel\Fortify\Http\Controllers, which has function:
public function store(Request $request): Responsable
{
$request->validate([Fortify::email() => 'required|email']);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$status = $this->broker()->sendResetLink(
$request->only(Fortify::email())
);
dd($status); // -> "passwords.user"
return $status == Password::RESET_LINK_SENT
? app(SuccessfulPasswordResetLinkRequestResponse::class, ['status' => $status])
: app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]);
}
/**
* Get the broker to be used during password reset.
*
* @return \Illuminate\Contracts\Auth\PasswordBroker
*/
protected function broker(): PasswordBroker
{
return Password::broker(config('fortify.passwords'));
}
(Fortify::email() fetches the setting in config/fortify -> 'email'.)
sendResetLink() function lives in an PasswordBroker-interface:
<?php
namespace Illuminate\Contracts\Auth;
use Closure;
interface PasswordBroker
{
/**
* Constant representing a successfully sent reminder.
*
* @var string
*/
const RESET_LINK_SENT = 'passwords.sent';
/**
* Constant representing a successfully reset password.
*
* @var string
*/
const PASSWORD_RESET = 'passwords.reset';
/**
* Constant representing the user not found response.
*
* @var string
*/
const INVALID_USER = 'passwords.user';
/**
* Constant representing an invalid token.
*
* @var string
*/
const INVALID_TOKEN = 'passwords.token';
/**
* Constant representing a throttled reset attempt.
*
* @var string
*/
const RESET_THROTTLED = 'passwords.throttled';
/**
* Send a password reset link to a user.
*
* @param array $credentials
* @param \Closure|null $callback
* @return string
*/
public function sendResetLink(array $credentials, Closure $callback = null);
/**
* Reset the password for the given token.
*
* @param array $credentials
* @param \Closure $callback
* @return mixed
*/
public function reset(array $credentials, Closure $callback);
This one: public function sendResetLink(array $credentials, Closure $callback = null);
That function receives credentials *), but is unable to retrieve the correct email since it is encrypted in the database. That function gives me a result "passwords.user" [checked with dd($status);] which refers to translation file at resources/lang/en/passwords -> 'user' => "We can't find a user with that email address." and throws back at resetlinkpage with that error.
Password reset works great if I just drop the encryption from email column.
Thoughts and questions:
*) What credentials? The email in this case? Can I change it to something else, e.g. the username if I retrieved it first with the email address? Am I missing something?
How can I go around this or should I just drop this case and change my setup back to non-encrypted email column? Is there any use to create another column with hashed email and compare to that first?
I'm stuck. I need other pairs of eyes and more brains. Please, help!