laravel - reset password for multiple accounts - laravel

I am using Laravel's "reset password".
There is something particular in our architecture: several accounts can have the same email address, the login is the unique key.
I would like to change the password reset controller so that, in password reset view:
- if the user put its email, the password is set for all accounts with this email (should I do it in a middleware? now only a random account is set, the first one I guess)
- if the user put its login, we change the password of its login only
Do you think this is possible? (for new accounts it will be impossible to create a new account with an existing email, but now we have about 8000 users with double email accounts, so this cannot be changed unfortunately).
thanks a lot in advance for your advices!
here is my code and I don't know where to start
[EDIT]
Here is my code after Mostakim Billah's suggestion:
I rewrote the existing resetPassword et reset function (let them as they were) and added the //HERE part
public function reset(Request $request)
{
$request->validate($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($request, $response)
: $this->sendResetFailedResponse($request, $response);
}
protected function resetPassword($user, $password)
{
$user->password = Hash::make($password);
$user->setRememberToken(Str::random(60));
$user->save();
// HERE: set passwords for other users with the same email
**User::where('email', $user->email)
->where('login', '!=', $user->login)
->where('password', null)
->update(['password' => Hash::make($password)]);**
event(new PasswordReset($user));
$this->guard()->login($user);
}

You can override reset method(which is in ResetsPasswords trait) in ResetPasswordController class and do whatever you want in this method.

Related

Set email verified to true if password reset is done

I'm trying to set email verified as true if the password reset is completed.
Currently, when a user (email not verified) requests a password reset, it does send an email and the user is able to change password.
As we can confirm that, email in fact belongs to that user, we should be able to set email verified to true. Currently, Laravel doesn't seem to know when an unverified email requests a password reset.
My reset function on ResetPasswordController.php is something like this(overridden to reset function of ResetsPasswords.php)
public function reset(Request $request)
{
$request->validate($this->rules(), $this->validationErrorMessages());
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$response = $this->broker()->reset(
$this->credentials($request),
function ($user, $password) {
$this->resetPassword($user, $password);
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $response == Password::PASSWORD_RESET
? $this->sendResetResponse($request, $response)
: $this->sendResetFailedResponse($request, $response);
}
How can I let laravel know that User now has a verified email?
Thank you
Laravel default "email_verified_at" is indeed a timestamp, so you can handle this in several ways:
in your reset method:
$response = $this->broker()->reset(
$this->credentials($request),
function ($user, $password) {
$this->resetPassword($user, $password);
$user->email_verified_at = Carbon\Carbon::now(); //add this line
$user->save(); //add this line
}
);
Now the user has a valid timestamp and you can "cast" it to a boolean like this in your User model:
On User.php model class:
//Some code
public bool isVerified(){
if(isset($this->email_verified_at)){
return true;
}
else{
return false;
}
}
Now you can use: $user->isVerified() to check if user has verified its email
Hope it helped!

How do I manually send a password reset request in Laravel 5.8

I would like to manually send a password reset request to a specific user (not the one currently logged in) from within a controller. I did some digging around in the Laravel code and I searched many articles but I do not get output.
//...
use Illuminate\Support\Facades\Password;
//...
public function sendResetEmail(Request $request)
{
// I will assueme that you already have $email variable
$response = Password::sendResetLink(['email' => $email], function (Message $message) {
$message->subject($this->getEmailSubject());
});
switch ($response) {
case Password::RESET_LINK_SENT:
dump('We have e-mailed your password reset link!');
case Password::INVALID_USER:
dump('We can\'t find a user with that e-mail address.');
}
}
You can do it using the Password facade
$email = 'example#domain.com';
$response = \Illuminate\Support\Facades\Password::broker()->sendResetLink($email);
$ok = $response == \Illuminate\Support\Facades\Password::RESET_LINK_SENT;

Laravel password reset with mongodb

I am migrating old project (done in zend framework) to laravel 5.5. Database is mongo db. I am using laravel-mongodb to connect laravel and mongo.
I already override laravel login functionality because table fields are not same as default laravel fields. Login is working fine.
At present when I try to reset password I am getting error message "We can't find a user with that e-mail address". How can I override reset password functionality?
In user table the field name are usrEmail and usrPassword. Working code of login is given below.
At present when I try to reset password I am getting error message We can't find a user with that e-mail address. How can I override reset password functionality?
In user table the field name are usrEmail and usrPassword. Working code of login is given below.
LoginController.php
protected function attemptLogin(Request $request)
{
$authUser = User::where('usrEmail', $request->email)
->whereIn('usrlId', [1, 2, 5, 6])
->first();
if($authUser) {
$password = md5(env('MD5_Key'). $request->password. $authUser->usrPasswordSalt);
$user = User::where('usrEmail', $request->email)
->where('usrPassword', $password)
->where('usrActive', '1')
->where('usrEmailConfirmed', '1')
->where('is_delete', 0)
->where('usrlId', 2)
->first();
if ($user) {
$updateLoginTime = User::find($user->_id);
$updateLoginTime->lastlogin = date('Y-m-d H:i:s');
$updateLoginTime->save();
$this->guard()->login($user, $request->has('remember'));
return true;
}
else {
return false;
}
}
return false;
}
Try placing this in your Auth/ResetsPasswordController.php
protected function credentials(Request $request)
{
$data = $request->only(
'password', 'password_confirmation', 'token'
);
$data['usrEmail'] = $request->get('email');
return $data;
}
By default the ->only( also includes the email field, but since it is different in your database we needed to override this function, which is by default defined in the ResetsPasswords trait.
This should ensure that any email field in the password reset flow (both on requesting the email and the form once you click the emailed link) will point to the right field in your database.

Laravel issue with loginUsingId (Manual Authentication)

I am trying to implement a single signon on multiple domains. The concept is pretty simple i.e to send unique user tokens and then verify these tokens to find the user and then log him in.
Now after verifying the token and then grabbing the user, i do something like this
$loggedInUser = Auth::loginUsingId($user->id, true);
Now i have a custom middleware where it first checks for a logged in user, i.e
Auth::Check()
The above works fine for the first time. But on refresh Auth::check() is not validated. I have also tried using all different session drivers but still doesn't work.
I used a similar code on laravel 5.2, and it did work. But on laravel 5.3 its not validating on persistent requests.
Edit: Let me show you my Code
I have not modified AuthServiceProvider or any other guard. I do have the user model inside a directory but i have modified the path in auth.php.
Here is the route that domain1 points to:
http://domain2.com/{{$role}}/{{$route}}/singlesignon/{{$token}}
This is then picked up by verifySingleSignOn method inside the loginController which takes in the role, route that the user came in from other domain and the token. The user is then redirected to the same routes, but on domain2. Here i can successfully recieve the user id before manually logging in.
public function verifySingleSignOn($role, $route, $token)
{
// Fetch Single Signon
$userRepository = new UserRepository();
$user = $userRepository->checkForSingleSignOnToken($token, ['id']);
// Check if Token Exists
if (isset($user->id) && is_int($user->id) && $user->id != 0) {
// Manually Logging a user (Here is successfully recieve the user id)
$loggedInUser = Auth::loginUsingId($user->id);
if (!$loggedInUser) {
// If User not logged in, then Throw exception
throw new Exception('Single SignOn: User Cannot be Signed In');
}
$redirectTo = $role . '/' . $route;
return redirect($redirectTo);
} else {
return Auth::logout();
}
}
Then i have this GlobalAdminAuth middleware
// Check if logged in
if( Auth::Check() ){
$user = Auth::User();
// Check if user is active and is a globaladmin
if( !$user->isGlobalAdmin() || !$user->isActive() ){
return redirect()->guest('login');
}
}else{
return redirect()->guest('login');
}
return $next($request);
Now the first time everything works fine and the user moves through the middleware successfully . but the second time the else statement is triggered.
Edit: Code for checkForSingleSignOnToken
public function checkForSingleSignOnToken($token, $columns = array('*'))
{
return User::where('single_signon', $token)->first($columns);
}
try
Auth::login($user);
instead of
Auth::loginUsingId($user->id, true);
Cookies are restricted domain-wise. Your application on domain1.com wont be able to grab cookies set by domain2.com.
You should be customizing the guard to use some other mechanism than cookies. Maybe use a token in the query parameters.
add this to your protected $middleware array in app\Http\Kernel.php
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class
I think it has to do with an update in the framework
no using auth:check in middleware
using request->user() or auth::user()
Please review bellow code structure, i had made manual authentication
in laravel 5.0.
routes.php
Route::get('login_user_by_id/{id?}', ['as' => 'login_user_by_id', 'uses' => 'UsersController#login_user_by_id']);
Route::post('user_login_post_for_admin',['as'=>'user_login_post_for_admin','uses'=>'LoginController#user_login_post_for_admin']);
Route::get('user_logout', ['as' => 'user_logout', 'uses' => 'UsersController#user_logout']);
LoginController.php
public function user_login_post_for_admin(){
$this->set_email($_POST['email']);
$this->set_password($_POST['password']);
$this->set_login_requested_role(['Admin','Moderator']);
return $this->user_login_post();
}
public function user_login_post(){
$User = new User();
if(isset($this->email) && !empty($this->email)){
$User->set_email(trim($this->email));
$User->set_password(Hash::make(trim($this->password)));
$user_login_data = $User->check_email_password_for_login();
if(isset($user_login_data) && !empty($user_login_data)){
if (Hash::check(trim($this->password), $user_login_data[0]->password)) {
$response['user_id']=$user_login_data[0]->id;
$response['name']=$user_login_data[0]->name;
$response['surname']=$user_login_data[0]->surname;
$response['profile_picture']=$user_login_data[0]->profile_picture;
$response['SUCCESS']='True';
$response['MESSAGE']='Login Success.';
return Redirect::route('login_user_by_id',[$user_login_data[0]->id]);
}else{
Session::put('SUCCESS','FALSE');
Session::put('MESSAGE', 'Invalid Credential.');
return redirect()->back();
}
}else{
Session::put('SUCCESS','FALSE');
Session::put('MESSAGE', 'Invalid Credential.');
return redirect()->back();
}
}else{
Session::put('SUCCESS','FALSE');
Session::put('MESSAGE', 'Invalid Credential.');
return redirect()->back();
}
}
UsersController.php
public function login_user_by_id($id=''){
if(isset($_GET['id'])&&!empty($_GET['id'])){
$id = $_GET['id'];
}
$User = new User();
$Log=new Log();
$user_for_auth = $User->find($id);
Auth::login($user_for_auth, true);
$User->id=AUTH::user()->id;
$auth_user_role=$User->auth_user_role();
$rl_title=$auth_user_role[0]->rl_title;
return Redirect::route('admin_home');
}
public function user_logout(User $user){
$User=new User();
$login_user_id = AUTH::user()->id;
$User->id=AUTH::user()->id;
$auth_user_role=$User->auth_user_role();
$login_user_role=$auth_user_role[0]->rl_title;
$response['user_id']=$login_user_id;
$response['SUCCESS']='TRUE';
$response['MESSAGE']='Successfully Logout.';
Auth::logout();
return Redirect::route('admin_login');
}

Cartalyst Sentry and registration user

It is possible to create user from Admin panel, by administrator without password? I imagine follow procedure:
Administrator create user without password
User get email with instruction for entering password and activation account
User can register with email and his password
I don't think so. That's why when I create my users I generate a random password.
$user->password = str_shuffle("Random_Password"); // generate random initial password
I have done this before by hacking the 'forgotten password' functionality of Laravel (rather that reinventing the wheel). I can't say how well this fits into Sentry but it was pretty trivial to do it in plain old Laravel:
Create user with blank password
Add an entry into the password reminders table (manually, don't use Auth::remind or whatever it is as it'll send an email, but do use the code from the class to generate the token)
Send welcome email to user with link to /user/confirm (or whatever, the point is that it doesn't have to be /user/forgotten-password) and hook that route up in the normal way for forgotten password with an added check for $user->password == '' if you wanna make sure only unconfirmed people can go to that page (not that it really matters).
You may also wish to extend the timeout on the forgotten passwords or, as I did (proper hacky I know), when the user's in the /user/confirm version of the forgotten password functionality, just refresh the timeout in the table before passing through to Laravel's auth system for checking.
Our code is something like this:
On register:
// however you register the user:
$user = new User;
$user->email = Input::get('email');
$user->password = '';
$user->save();
// create a reminder entry for the user
$reminderRepo = App::make('auth.reminder.repository');
$reminderRepo->create($user);
Mail::send(
'emails.registered',
[
'token' => $reminder->token,
],
function ($message) use ($user) {
$message->to($user->email)->setSubject(Lang::get('account.email.registered.subject', ['name' => $user->name]));
}
);
Now the confirm link:
class AccountController extends Controller
{
public function confirm($token)
{
$reminder = DB::table('password_reminders')->whereToken($token)->first();
if (! $reminder) {
App::abort(404);
}
// reset reminder date to now to keep it fresh
DB::table('password_reminders')->whereToken($token)->update(['created_at' => Carbon\Carbon::now()]);
// send token to view but also email so they don't have to type it in (with password reminders it's is a good thing to make users type it, but with confirm account it feels weird)
return View::make('account.confirm-account')->withToken($token)->withEmail($reminder->email);
}
public function postConfirm($token)
{
$credentials = Input::only('email', 'password', 'password_confirmation', 'token');
$response = Password::reset($credentials, function ($user, $password) {
$user->password = $password;
$user->save();
});
switch ($response) {
case Password::INVALID_PASSWORD:
case Password::INVALID_TOKEN:
case Password::INVALID_USER:
return Redirect::back()->withInput()->with('message-error', Lang::get($response));
case Password::PASSWORD_RESET:
Auth::login(User::whereEmail(Input::get('email'))->first());
return Redirect::route('account.home')->with('message-info', Lang::get('messages.confirm_account.succeeded'));
}
}

Resources