How ResetPasswordController works with password_reset table Laravel - php

The built-in controller for resetting the password Auth \ Reset Password Controller has the reset function
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);
}
Well, here we are working with the user and their incoming data. However, I can't understand where the work with the password_resets table (built-in) is going? After all, after password recovery, entries are added/deleted there. I think (maybe incorrectly) that this is implemented in the broker () method, but I can't find it in the hierarchy of traits, interfaces, and other classes.

Checkout /vendor/laravel/framework/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php
This is the default class that implements the TokenRepositoryInterface and is used by the /vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordBroker.php.
Inside this class you can find all the actual functionality that handles the password resets including the table operations you mention. For example, one method you'll find is:
/**
* Create a new token record.
*
* #param \Illuminate\Contracts\Auth\CanResetPassword $user
* #return string
*/
public function create(CanResetPasswordContract $user)
{
$email = $user->getEmailForPasswordReset();
$this->deleteExisting($user);
// We will create a new, random token for the user so that we can e-mail them
// a safe link to the password reset form. Then we will insert a record in
// the database so that we can verify the token within the actual reset.
$token = $this->createNewToken();
$this->getTable()->insert($this->getPayload($email, $token));
return $token;
}

Related

My login route returns "unauthorised access" Laravel

So I'm trying to make a laravel API for a escorts-like site, anyway, i use Passport for authentification and the register part works but the login one doesnt, and i dont know why, i'll let the passportAuthController down as code and a ss of the database
class passportAuthController extends Controller
{
/**
* handle user registration request
*/
public function registerUserExample(RegisterUserRequest $request){
///TODO: TEST THE CRUD FEATURES IMPLEMENTED IN THE USER CONTROLLER AFTER U CHECK LOGIN FEATURE
$attributes = $request -> validated();
$user = User::create($attributes);
$access_token_example = $user->createToken('RegisterToken')->accessToken;
//return the access token we generated in the above step
return response()->json(['token'=>$access_token_example],200);
}
/**
* login user to our application
*/
public function loginUserExample(Request $request){
$login_credentials=[
'email'=>$request->email,
'password'=>$request->password,
];
if(auth()->attempt($login_credentials)){
//generate the token for the user
$user_login_token= auth()->user()->createToken('LoginToken')->accessToken;
//now return this token on success login attempt
return response()->json(['token' => $user_login_token], 200);
}
else{
//wrong login credentials, return, user not authorised to our system, return error code 401
return response()->json(['error' => 'UnAuthorised Access'], 401);
}
}
/**
* This method returns authenticated user details
*/
// index function
public function authenticatedUserDetails(){
//returns details
return response()->json(['authenticated-user' => auth()->user()], 200);
}
}
The request as well:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class RegisterUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name'=>'required|max:255|min:3',
'email'=>'required|email',
'password'=>'required|min:7|max:255',
'gender'=>'required|min:4|max:6',
'interest'=>'required|min:4|max:6',
'Country'=>'required|max:255',
'County'=>'required|max:255',
'City'=>'required|max:255',
'birthday'=>'required|date'
];
}
}
and the ss of the database:
and the routes (api.php):
<?php
use App\Http\Controllers\passportAuthController;
use App\Http\Controllers\UserController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
//routes/api.php
//login & register routes
Route::post('register',[passportAuthController::class,'registerUserExample']);
Route::post('login',[passportAuthController::class,'loginUserExample']);
//CRUD and search routes
Route::post('storeUser',[UserController::class,'store']);
Route::get('showAll',[UserController::class, 'index']);
Route::put('updateUser/{id}',[UserController::class,'update']);
Route::delete('delete/{id}', [UserController::class,'deleteUser']);
Route::get('search/{name}',[UserController::class,'search']);
//add this middleware to ensure that every request is authenticated
Route::middleware('auth:api')->group(function(){
Route::get('user', [passportAuthController::class,'authenticatedUserDetails']);
});
Your password in users table is not encrypted.
The reason is this line
$attributes = $request->validated();
$user = User::create($attributes);
You have not encrypted your password and the method auth()->attempt($login_credentials) uses compares the encrypted password request with stored encrypted password in your db.
You can use bcrpyt() to encrypt your password, laravel comes with bcrypt() as a helper function.
Change to this in your registerUserExample(RegisterUserRequest $request)
$attributes = $request->validated();
foreach($attributes as $key => $attribute){
if($key == 'password') {
$attributes[$key] = bcrypt($attribute);
}
}
$user = User::create($attributes);
so if you see the response is mean that wrong login credentials, return, user not authorised to our system, return error code 401 ,
So with a little observation you will know that your code work fine but your logic is not good ,
So the answer simply is because the password insert in your database is note crypted and laravel passport when they are trying to make login they use a function of check ,
so if you want your code work your password must be crypted in the register exemple
$user->password = hash::make($request->password);
Or
$user->password = Crypt::encrypt($request->password);
Conclusion you can't make authentification with laravel passport if your password not crypted
The attempt method accepts an array of key / value pairs as its first argument. The password value will be hashed. The other values in the array will be used to find the user in your database table. So,
You try this
public function loginUserExample(Request $request){
$user = User::where('account', $request->account)
->where('password', $request->password)
->first();
if($user) {
Auth::loginUsingId($user->id);
// -- OR -- //
Auth::login($user);
return redirect()->route('home');
} else {
return redirect()->back()->withInput();
}
}

Laravel get plain text password when user enters the password reset form

While doing a Password reset in laravel I have a need to also obtain the password the user enters on the password and confirm password fields , I need this because i have to post this to another api to update there password over there as well .
Can you please let me know how I can access this .
I have checked in controller's Auth ResetPasswordcontroller.php but i cannot figure out how to intercept and get the plain text password but still alow the normal password reset to occur.
You can simply override the reset() method from the ResetsPasswords trait within the controller.
ResetPasswordController.php
class ResetPasswordController extends Controller
{
use ResetsPasswords;
// ...
public function reset(Request $request)
{
// the code in this section is copied from ResetsPasswords#reset
$request->validate($this->rules(), $this->validationErrorMessages());
// --- put your custom code here ------------
$plaintext_password = $request->password;
// --- end custom code ----------------------
// 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 to extend or make custom PasswordBroker sendResetLink() method in Laravel 5.8?

Currently the logic behind Resetting Password is that user must provide valid/registered e-mail to receive password recovery e-mail.
In my case I don't want to validate if the e-mail is registered or not due to security concerns and I want to just do the check in back-end and tell user that "If he has provided registered e-mail, he should get recovery e-mail shortly".
What I've done to achieve this is edited in vendor\laravel\framework\src\Illuminate\Auth\Passwords\PasswordBroker.php sendResetLink() method from this:
/**
* Send a password reset link to a user.
*
* #param array $credentials
* #return string
*/
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
if (is_null($user)) {
return static::INVALID_USER;
}
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
return static::RESET_LINK_SENT;
}
to this:
/**
* Send a password reset link to a user.
*
* #param array $credentials
* #return string
*/
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
// if (is_null($user)) {
// return static::INVALID_USER;
// }
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
if(!is_null($user)) {
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
}
return static::RESET_LINK_SENT;
}
This hard-coded option is not the best solution because it will disappear after update.. so I would like to know how can I extend or implement this change within the project scope within App folder to preserve this change at all times?
P.S. I've tried solution mentioned here: Laravel 5.3 Password Broker Customization but it didn't work.. also directory tree differs and I couldn't understand where to put new PasswordBroker.php file.
Thanks in advance!
Here are the steps you need to follow.
Create a new custom PasswordResetsServiceProvider. I have a folder (namespace) called Extensions where I'll place this file:
<?php
namespace App\Extensions\Passwords;
use Illuminate\Auth\Passwords\PasswordResetServiceProvider as BasePasswordResetServiceProvider;
class PasswordResetServiceProvider extends BasePasswordResetServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerPasswordBroker();
}
/**
* Register the password broker instance.
*
* #return void
*/
protected function registerPasswordBroker()
{
$this->app->singleton('auth.password', function ($app) {
return new PasswordBrokerManager($app);
});
$this->app->bind('auth.password.broker', function ($app) {
return $app->make('auth.password')->broker();
});
}
}
As you can see this provider extends the base password reset provider. The only thing that changes is that we are returning a custom PasswordBrokerManager from the registerPasswordBroker method. Let's create a custom Broker manager in the same namespace:
<?php
namespace App\Extensions\Passwords;
use Illuminate\Auth\Passwords\PasswordBrokerManager as BasePasswordBrokerManager;
class PasswordBrokerManager extends BasePasswordBrokerManager
{
/**
* Resolve the given broker.
*
* #param string $name
* #return \Illuminate\Contracts\Auth\PasswordBroker
*
* #throws \InvalidArgumentException
*/
protected function resolve($name)
{
$config = $this->getConfig($name);
if (is_null($config)) {
throw new InvalidArgumentException(
"Password resetter [{$name}] is not defined."
);
}
// The password broker uses a token repository to validate tokens and send user
// password e-mails, as well as validating that password reset process as an
// aggregate service of sorts providing a convenient interface for resets.
return new PasswordBroker(
$this->createTokenRepository($config),
$this->app['auth']->createUserProvider($config['provider'] ?? null)
);
}
}
Again, this PasswordBrokerManager extends the base manager from laravel. The only difference here is the new resolve method which returns a new and custom PasswordBroker from the same namespace. So the last file we'll create a custom PasswordBroker in the same namespace:
<?php
namespace App\Extensions\Passwords;
use Illuminate\Auth\Passwords\PasswordBroker as BasePasswordBroker;
class PasswordBroker extends BasePasswordBroker
{
/**
* Send a password reset link to a user.
*
* #param array $credentials
* #return string
*/
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
// if (is_null($user)) {
// return static::INVALID_USER;
// }
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
if(!is_null($user)) {
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
}
return static::RESET_LINK_SENT;
}
}
As you can see we extend the default PasswordBroker class from Laravel and only override the method we need to override.
The final step is to simply replace the Laravel Default PasswordReset broker with ours. In the config/app.php file, change the line that registers the provider as such:
'providers' => [
...
// Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
App\Extensions\Passwords\PasswordResetServiceProvider::class,
...
]
That's all you need to register a custom password broker. Hope that helps.
The easiest solution here would be to place your customised code in app\Http\Controllers\Auth\ForgotPasswordController - this is the controller that pulls in the SendsPasswordResetEmails trait.
Your method overrides the one provided by that trait, so it will be called instead of the one in the trait. You could override the whole sendResetLinkEmail method with your code to always return the same response regardless of success.
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);
// 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.
$response = $this->broker()->sendResetLink(
$request->only('email')
);
return back()->with('status', "If you've provided registered e-mail, you should get recovery e-mail shortly.");
}
You can just override the sendResetLinkFailedResponse method in your ForgetPasswordController class.
protected function sendResetLinkFailedResponse(Request $request, $response)
{
return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT);
}
We'll just send the successful response even if the validation failed.

Insert data and Login in laravel

I am working on an assignment in laravel where I've an Application form. I want to submit application form with email, mobileNo, customerId etc.
What I want is to insert form data into users table and then user will be logged in with auto generated password and redirect to Customer's dashboard. Where will be a modal will be open and ask for add password.
On the other hand there is also a login page from where user can login as usual. The login functionality is working properly.
Can someone help me to achieve the above functionality. Thanks in advance.
**Data is : **
email='user#gmail.com'
mobile='9875425698'
customerId='CI10001';
ApplicationForm Controller Where I am getting data successfully
class ApplicationForm extends Controller
{
public function saveApplicationForm(Request $request){
return $request;
}
}
Add user by submiting form
$password = bcrypt('secret'); //add here random password
$user = new User();
$user->email = 'xyz#gmail.com';
$user->mobileNo = '123456789';
$user->customerId = '1245';
$user->password = $password;
$user->save();
after you insert raw on user table login by user id without password
Auth::loginUsingId($user->id);
Auth::loginUsingId($user->id,true); // Login and "remember" the given user...
by otherwise login with email and password
Auth::attempt(['email' => $user->email, 'password' => $password], $remember);
all action do in one method(action)
Following my comment:
In the RegisterController (App\Http\Controllers\Auth)
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'institution' => $data['institution'],
'password' => 'NOT_SET',
]);
}
Then create a middleware (e.g. php artisan make:middleware Must_have_password)
namespace App\Http\Middleware;
use Closure;
use Auth;
class Must_have_password
{
/**
* Verify if password is set, otherwise redirect to password-set page.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = Auth::user();
if ($user && $user->password !== 'NOT_SET') {
return $next($request);
}
else return redirect('/set-password');
}
}
Of course, you then need to create a password setting view and hook that to the /set-password route. As I said in the comment, you want to make sure that /set-password route is well protected because you don't want people hijacking accounts that way. The good thing about this approach (using NOT_SET) is that people can always use the password_reset infrastructure to reset their password if they don't do it initially.
This is a bit hacky, but because Laravel always encrypts the passwords, there is no way the value can become NOT_SET in another way. Alternatively, you could add a boolean to your user-model (something like Must_Reset) that redirects to the password-reset page.
You can also hook in the password-reset functionality of Laravel, look for 'One Time Password Laravel' (e.g. here).

PHP - Where to implement Session Logic in MVC?

Access to my application (in order)
Whitelist ip address
redirect to 404 on invalid ip
Check if last activity was > 2 hours ago
redirect to login page and expire session
Check if user is logged in, by looking at user data in $_SESSION
redirect to login page if not valid
Index.php
(notice it is very similar to this question):
/**
* Set Timezone
*/
date_default_timezone_set('Zulu');
/**
* Include globals and config files
*/
require_once('env.php');
/*
* Closure for providing lazy initialization of DB connection
*/
$db = new Database();
/*
* Creates basic structures, which will be
* used for interaction with model layer.
*/
$serviceFactory = new ServiceFactory(new RepositoryFactory($db), new EntityFactory);
$serviceFactory->setDefaultNamespace('\\MyApp\\Service');
$request = new Request();
$session = new Session();
$session->start();
$router = new Router($request, $session);
/*
* Whitelist IP addresses
*/
if (!$session->isValidIp()) {
$router->import($whitelist_config);
/*
* Check if Session is expired or invalid
*/
} elseif (!$session->isValid()) {
$router->import($session_config);
/*
* Check if user is logged in
*/
} elseif (!$session->loggedIn()) {
$router->import($login_config);
} else {
$router->import($routes_config);
}
/*
* Find matched route, or throw 400 header.
*
* If matched route, add resource name
* and action to the request object.
*/
$router->route();
/*
* Initialization of View
*/
$class = '\\MyApp\\View\\' . $request->getResourceName();
$view = new $class($serviceFactory);
/*
* Initialization of Controller
*/
$class = '\\MyApp\\Controller\\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view);
/*
* Execute the necessary command on the controller
*/
$command = $request->getCommand();
$controller->{$command}($request);
/*
* Produces the response
*/
echo $view->render();
The $router->import() function takes a json file with the route configuration and creates the routes (Haven't decided if I'm going to keep that). My router is a modified version of Klein.
My Question
Is this a proper implementation of how to check the session data?
I would prefer to check that the user data in the session can be found in the database, but I would need to use a Service for that, and Services should only be accessed by the controller(?). I wouldn't know which controller to send the user to since the route configuration would change if the user was logged in.
For example, if someone was trying to go to www.myapp.com/orders/123, I would send them to the Orders controller if they were logged in, or the Session controller (to render the login page) if they weren't.
I have read the ACL Implementation from this question. But, unless I'm mistaken, that is for controlling access for users who are already logged in, not for users who aren't logged in. If this is not the case, could someone please explain how I would implement my ACL to check this?
I greatly appreciate any help since the search for this answer has given me really mixed solutions, and most of them I don't like or don't seem like clean solutions. Like a Session Manager, which is basically what I'm doing, but pretending not to. =/
UPDATED index.php (my solution)
/**
* Set Timezone
*/
date_default_timezone_set('Zulu');
/**
* Include globals and config files
*/
require_once('env.php');
/*
* Closure for providing lazy initialization of DB connection
*/
$db = new Database();
/*
* Creates basic structures, which will be
* used for interaction with model layer.
*/
$serviceFactory = new ServiceFactory(new MapperFactory($db), new DomainFactory);
$serviceFactory->setDefaultNamespace('\\MyApp\\Service');
include CONFIG_PATH.'routes.php';
$request = new Request();
$router = new Router($routes,$request);
/*
* Find matched route.
*
* If matched route, add resource name
* and command to the request object.
*/
$router->route();
$session = $serviceFactory->create('Session');
/*
* Whitelist Ip address, check if user is
* logged in and session hasn't expired.
*/
$session->authenticate();
/*
* Access Control List
*/
include CONFIG_PATH.'acl_settings.php';
$aclFactory = new AclFactory($roles,$resources,$rules);
$acl = $aclFactory->build();
$user = $session->currentUser();
$role = $user->role();
$resource = $request->getResourceName();
$command = $request->getCommand();
// User trying to access unauthorized page
if (!$acl->isAllowed($role, $resource, $command) {
$request->setResourceName('Session');
$request->setCommand('index');
if ($role === 'blocked') {
$request->setResourceName('Error');
}
}
/*
* Initialization of View
*/
$class = '\\MyApp\\View\\' . $request->getResourceName();
$view = new $class($serviceFactory, $acl);
/*
* Initialization of Controller
*/
$class = '\\MyApp\\Controller\\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view, $acl);
/*
* Execute the necessary command on the controller
*/
$command = $request->getCommand();
$controller->{$command}($request);
/*
* Produces the response
*/
$view->{$command}
$view->render();
I start the session and authorize the user in the Session model. The session's currentUser will have a role of 'guest' if not logged in, or 'blocked' if it's IP address is not in the whitelist. I wanted to implement the Controller wrapper as suggested teresko's previous ACL post, but I needed something that would redirect the user's instead. I send them to their homepage (Session#index) if they try to access a page they aren't allowed to, or to Error#index if they are blocked. Session#index will let the View decide whether or not to display the homepage for a logged in user, or the login page if they aren't logged in (by checking the user's role). Maybe not the best solution, but doesn't seem too terrible.
Single Responsibility
Your session object is doing too many things. Sessions are more or less just for persistence across requests. The session shouldn't be doing any authentication logic. You store an identifier for the logged in user in the session, but the actual validation, logging in/out should be done in an authentication service.
Route Management
Importing different routes based on the users authentication status will not scale well and will be a pain to debug later when you have a lot more routes. It would be better to define all your routes in one place and use the authentication service to redirect if not authorized. I'm not very familiar with that router but looking at the documentation you should be able to so something like
$router->respond(function ($request, $response, $service, $app) {
$app->register('auth', function() {
return new AuthService();
}
});
Then on routes you need to be logged in for you can do something like
$router->respond('GET', '/resource', function ($request, $response, $service, $app) {
if( ! $app->auth->authenticate() )
return $response->redirect('/login', 401);
// ...
});

Categories