I'm trying to find a user form a database with 'email' = $email and send them an email. When I try to echo the $user, I receive an error that it's not a string.
public function sendEmail($user)
{
Mail::to($user['email'])->Send(new VerifyEmail($user));
}
public function verifyEmail($email)
{
$user = User::where('email',$email);
$this->sendEmail($user ); //mail won't send
return view('auth.email.verifyemail')->with('email', $email);
}
Please help, thank you!
Your code has a few more issues than not being able to find the user, but let's address this first.
Explanation
Calling the where() method on the User::class is returning an instance of Illuminate\Database\Query\Builder and not the record that you are looking for.
To get a collection of records you can call fluently the get() method or in your case you can just get the first result by calling first().
More on the topic: Retrieving Single Models / Aggregates
Solution
$user = User::where('email', '=', $email)->first();
if(!$user) {
// handle the case if no user is found
}
echo $user->email // access the user's email address
Then you can call your sendmail method or whatever you need and pass the $user instance like this $this->sendmail($user)
$thisUser has only a query. You need to execute it to get the user. Try to get the first record:
$thisUser = User::where('email',$email)->first();
I've been searching the internet and have yet to find a solution to the following problem...
We currently have a website developed using Laravel which the user table is a remote Microsoft SQL database. The driver in config/auth.php has been set to "database". All is working fine except for the password reset functionality, which we get the following error:
UnexpectedValueException in PasswordBroker.php line 238: User must implement CanResetPassword interface.
From my limited understanding of Laravel (this is my first experiance with Laravel), the Eloquent driver has support for the CanResetPassword functionality, however, this has not been implemented in the Database User Provider by Laravel, hence the error.
So my question is thus, has anyone had a configuration where they have the driver to “Database” and implemented a reset password functionality? All the examples I have seen to date relate to using the Eloquent model, which from my understanding of Laravel is not an option since during the initial development we had to change the driver from Eloquent to database to get the remote Microsoft SQL server working in the first place. Moving the Microsoft SQL database to a local database is not an option I’m afraid.
Alternatively, if anyone has implemented another method of a user resetting their password using an email address I would be open to suggestions.
To write your own password reset logic, you can still use the default migration that comes out of the box or simply create yours. The most important part is the token. Because you are making your own password reset, you have a couple of decisions to make:
Will the token expire?
Can a user use the same token multiple times?
You will need 2 pages, 4 different routes and 4 different functions in the same controller. The 'I forgot my password' page and the 'Reset password' page. In the first page, display a form where you take the user email. And post to the following controller.
//to be added on top as use statements
use DB;
use Auth;
use Hash;
use Carbon;
use App\User;
public function sendPasswordResetToken(Request $request)
{
$user = User::where ('email', $request->email)-first();
if ( !$user ) return redirect()->back()->withErrors(['error' => '404']);
//create a new token to be sent to the user.
DB::table('password_resets')->insert([
'email' => $request->email,
'token' => str_random(60), //change 60 to any length you want
'created_at' => Carbon::now()
]);
$tokenData = DB::table('password_resets')
->where('email', $request->email)->first();
$token = $tokenData->token;
$email = $request->email; // or $email = $tokenData->email;
/**
* Send email to the email above with a link to your password reset
* something like url('password-reset/' . $token)
* Sending email varies according to your Laravel version. Very easy to implement
*/
}
Second part, when the user clicks on the link
/**
* Assuming the URL looks like this
* http://localhost/password-reset/random-string-here
* You check if the user and the token exist and display a page
*/
public function showPasswordResetForm($token)
{
$tokenData = DB::table('password_resets')
->where('token', $token)->first();
if ( !$tokenData ) return redirect()->to('home'); //redirect them anywhere you want if the token does not exist.
return view('passwords.show');
}
Display a page with a form containing 2 inputs
- New password password or whateveer you want
- New password confirmation password_confirm or whatever you want
The form should post to the same URL mapped to the following controller. Why? because we still need to use the token to find the actual user.
public function resetPassword(Request $request, $token)
{
//some validation
...
$password = $request->password;
$tokenData = DB::table('password_resets')
->where('token', $token)->first();
$user = User::where('email', $tokenData->email)->first();
if ( !$user ) return redirect()->to('home'); //or wherever you want
$user->password = Hash::make($password);
$user->update(); //or $user->save();
//do we log the user directly or let them login and try their password for the first time ? if yes
Auth::login($user);
// If the user shouldn't reuse the token later, delete the token
DB::table('password_resets')->where('email', $user->email')->delete();
//redirect where we want according to whether they are logged in or not.
}
Don't forget to add routes
Route::get('password-reset', 'PasswordController#showForm'); //I did not create this controller. it simply displays a view with a form to take the email
Route::post('password-reset', 'PasswordController#sendPasswordResetToken');
Route::get('reset-password/{token}', 'PasswordController#showPasswordResetForm');
Route::post('reset-password/{token}', 'PasswordController#resetPassword');
Note: There might be typos or syntax errors because I did not test this and wrote it here directly from the top of my head. If you see an error/exception, don't panick, read the error and search google.
Just to add to what #eddythedove said.
Instead of str_random(60) I used the Laravel way of creating a token:
private function generateToken()
{
// This is set in the .env file
$key = config('app.key');
// Illuminate\Support\Str;
if (Str::startsWith($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
return hash_hmac('sha256', Str::random(40), $key);
}
If you find an error in str_random, make sure you import the module first:
use Illuminate\Support\Str;
Then call with Str::random (60).
$key = config('app.key');
if (Str::startsWith($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
$token = hash_hmac('sha256', Str::random(40), $key);
$dbToken = app(Hasher::class)->make($token);
DB::insert('password_resets', [
'email' => 'email#mail.com',
'token' => $dbToken,
]);
This should work in Laravel 8
The default way Laravel handles the Reset Password has a few security issues.
No track record reset password attempts (Delete the token in the table after success attempt is not acceptable)
No expiry date
No token used time
We always better keep track of these security functions.
I have altered the default table like this on my db migration:
public function up()
{
Schema::table('password_resets', function (Blueprint $table) {
$table->bigIncrements('id');
$table->enum('is_used', ['t', 'f'])->default('f');
$table->dateTime('updated_at')->nullable();
});
}
Instead of deleting the record I simply update the table 'is_used' to 't' and updated_at column.
I use following query to filter is_used = 'f' and created on the same day to gather with token.
$data = PasswordReset::where('token', $token)->where('is_used', 'f')
->whereDate('created_at', '>=', Carbon::today()->toDateString())->first();
i try some code after that i get some solution that will work in laravel 8+.
$key = config('app.key');
if (Str::startsWith($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
$token = hash_hmac('sha256', Str::random(40), $key);
$dbToken =Hash::make($token);
Within my project, I have given the administrator role the privilege to add users to the site. For security reasons, I hash a random string, to store as the temporary password, I then want to send the user an email with the standard Laravel reset password template.
I have the following:
$user = new User();
$user->name = Input::get('name');
$user->email = Input::get('email');
$user->password = Hash::make(str_random(8));
$user->save();
$response = Password::sendResetLink(Input::get('email'), function (Message $message) {
$message->subject('Password Reset');
});
The error I'm getting is
Argument 1 passed to
Illuminate\Auth\Passwords\PasswordBroker::sendResetLink() must be of
the type array, string given
How can I trigger this function within Laravel, so the user is sent a password reset email? Thank you.
The problem here is that you are sending string email, and you should send array (this is what error says).
You should in this case use:
Request::only('email')
instead of
Input::get('email')
I've got a simple CRUD application in which is possible to add users, through a form, the ideal flow is
an admin fills the form with user name and password
an email is sent to the user with a link to set the password
the user is logged in
Now at the end of the day what I really want is that the PasswordBroker does, but instead to send the emails.passsowrd view I want a different one, and the rest of the logic can be the same
Is there an easy way so create a valid isntace of the passwordBroker and passing a different view?
the property emailWiew is protected so I cannot override it, I tried to create a new instance out of the IoC
$tokens = $this->app['auth.password.tokens'];
$users = $this->app['auth']->driver()->getProvider();
$view = "emails.createPassword";
$password = new PasswordBroker(
$tokens, $users, $this->app['mailer'], $view
);
dd($users->retrieveByCredentials(["email#user.com"])); //i get null and the user email is valid
$response = $password->sendResetLink(["email#user.com"], function (Message $message) {
$message->subject("Set your password");
});
dd($response); // I get "passwords.user" wich is an error
but when I pass the email address I get an invalid user error
any idea?
The problem is that you're not providing the key to retrieve the credentials, so it's trying to get a field in the users table by the name of "0". use the following and it will work:
$response = $password->sendResetLink(
["email" => "email#user.com"],
function (Message $message) {
$message->subject("Set your password");
});
I am trying to change the password of a user for this i need to check that old password of that user does match with the value i am getting from html "oldpass" text box. If the existing password value and "oldpass" value matches the new password will be updated in database.The password value i am getting from the database is encrypted.
$userpass = User::where('id', '=', Session::get('userid'))->get(array('password'));
The problem is that $userpass returns a null value .
Here is the code:
$oldpass = Hash::make(Input::get('oldpass'));//Getting password from html form
$userpass = User::where('id', '=', Session::get('userid'))->get(array('password'));//Getting password value from database Users table
if ($oldpass === $userpass) {
User::where('id', '=', Session::get('userid'))
->update(array(
'password' => Hash::make(Input::get('newpass'))
));
} else {
Return View::make('changepass.changepass')
->with('errormessage', 'Password does not match');
}
There are two main problems here.
On one hand, $userpass is returning null because get() is not the appropiate function to fecth a column. You can use pluck for that (see the query builder docs)
Anyway you can just call the attribute once you fetch the user like:
$userpass = User::find(Session::get('userid'))->password;
You are trying to compare a hashed password with a plain password. Laravel uses Guard
by default to manage user Authentication and Guard uses Hash::make to store it. You should compare hashes with:
Hash::check($oldpass, $userpass)
You could also just check with Guard the user credentials are correct with Auth::validate($credentials) (see Laravel Security) and then change the password like:
if(Auth::validate('id' => Session::get('userid'), 'password' => Input::get('oldpass'))){
//Assuming user was authenticated before. If not use Auth::attempt instead of validate
Auth::user()->password = Hash::make(Input::get('newpass'));
} else {
Return View::make('changepass.changepass')
->with('errormessage', 'Password does not match');
}