Disable hashing on Auth::attempt - laravel

I am working with an old database without hashed passwords, also this database needs to be unhashed since it is connected to a Runnable JAR.
I did everything to connect it with Laravel 5.3 and it worked, but.. When comes to login it always return false.
Here is the function code:
public function login(Request $request)
{
$this->validate($request, [
'account' => 'required|alpha_num|exists:accounts,account',
'password' => 'required|alpha_num|min:4',
]);
if(Auth::attempt(['account' => $request->account, 'password' => $request->password])){
return redirect()->route('account');
}
return redirect()->back()->withInput();
}
I came to the conclusion that Auth::attempt hashes the given password through the view and when comparing to the unhashed one in the database, returns false.
How can i fix this??
Thank you.

You will need to use another method of manual authentication.
$user = User::where('account', $request->account)
->where('password', $request->password)
->first();
if($user) {
Auth::loginUsingId($user->id);
// -- OR -- //
Auth::login($user);
return redirect()->route('account');
} else {
return redirect()->back()->withInput();
}

You just can add this to your App/User.
If you are using another driver in config/hashing.php - change bcrypt to argon/argon2i
public function getAuthPassword() {
return bcrypt($this->password);
}

Related

Hash password after Validator::make

I want to hash the password after validating the data.
My code:
public function create(Request $request){
$data = Validator::make($request->only(['name', 'email', 'password']), [
'name' => 'required|min:3:max:20',
'email' => 'required|email|unique:users',
'password' => 'required|min:6',
]);
if ($data->fails()) {
//Do something
}else{
User::create($data);
}
}
So how to hash the password after validation?
I tried to override the password inside $data, But it's not working
$data->safe()->only('password') = Hash::make($data->safe()->only('password'));
I can't use $request['password'] as I won't be able to validate it and check if it's empty ..etc.
An alternative approach would be to use an Eloquent Mutator to automatically hash the password field when it is set.
// User Model
public function setPasswordAttribute($value): void
{
$this->attributes['password'] = Hash::make($value);
}
I personally like this approach because you won't have to worry about it in the controllers. You can just set it once and forget about it. :)
The quick answer to your question would be to use the Eloquent make function.
$user = User::make($data);
$user->password = Hash::make($password);
$user->save();
Where $password is where ever you have the password stored. In your case:
$password = $data->safe()->only('password')
There may be a more efficient way, based on your exact intent. In general, the above solution will work.
The make function creates an Eloquent model, but does not store it in the database. That's why you can edit the values and then call $user->save()
Use a mutator method to set the password. Override the method by adding:
public function setPasswordAttribute($value)
{
$this->attributes['password'] = 'some random password generator';
}
there is document:
https://laravel.com/docs/8.x/eloquent-mutators#defining-a-mutator

Laravel: How can I return more data from a custom UserProvider?

I have successfully created a custom user provider and have implemented a user interface.
I am trying to save the user object to my local Postgres database that is returned from a third party backend.
Here is what my retrieveByCredentials method looks like:
// MyCustomUserProvider.php
public function retrieveByCredentials(array $credentials) {
if (empty($credentials)) {
return null;
}
try {
$response = Http::asForm()
->retry(3, 100)
->timeout(5)
->post('https://my-custom-auth-endpoint', $credentials)
->json();
if ($response) {
$user = User::firstOrNew([
'foo' => $response['foo'],
]);
$user->save();
return $user;
}
} catch (RequestException $e) {
Log::info('-----------------------------------------------------------');
Log::info(print_r($e->getMessage(), true));
Log::info('-----------------------------------------------------------');
return $e->getMessage();
}
}
...
public function validateCredentials(Authenticatable $user, array $credentials): bool {
return strtoupper($user->foo) === strtoupper($credentials['foo']);
}
So far, everything is working great. I am getting the correct values/booleans for each required method. However, I need to be able to return additional info to my controller than just a boolean value. For example, if a user's password has expired, this information is coming from my 3rd party database. So far, it looks like returning the user results in a true or false response in my LoginController. If I get false back from my CustomProvider I can set a flash message of, "Invalid Credentials". But that may not always be the appropriate error.
Here is how I am calling the CustomUserProvider:
// LoginController.php
$authenticated = Auth::guard('foo')
->attempt([
'userid' => $request->username,
'password' => $request->password,
]);
// $authenticated is the response from CustomUserProvider.
dd($authenticated) // true or false.
It is understood that in my provider I just want to authenticate the user. How can I return the authenticated user back to the controller? Additionally, how can I return the error back to the controller so I can display a proper error message?
The function Auth::guard('foo')->attempt([]) will also return a boolean to tell user if the authentication is successful or not. If you want to get the auth user, just return Auth::user() after the calling that function, like return response()->json(Auth::user())

Laravel Password Basic Hashing

I want to encrypt password in Laravel.But Hash or Crypt are encryting again again every page refresh. So I dont use it.
$pass = Hash::make($user_password);
$pass = Crypt::encrypt($user_password);
How can i do one times encrypt with Hash or Crypt method ? Because I can't do login page with Hash.
I solved this problem
public function login(Request $request)
{
$email = $request->input('user_email');
$password = $request->input('user_password');
$user = BO_USER::where('email', '=', $email)->first();
if (!$user) {
return response()->json(['success'=>false, 'message' => 'Login Fail, please check email id']);
}
if (!Hash::check($password, $user->password)) {
return response()->json(['success'=>false, 'message' => 'Login Fail, pls check password']);
}
//return response()->json(['success'=>true,'message'=>'success', 'data' => $user]);
return redirect('/dashboard');
}
You will use the Hash, cause Crypt can be decrypted.
See documentation:
Hashing for password
Encrypt and decrypt values

How to add expiry date condition to login function in laravel 5.2

In laravel 5.2, i want to add the condition so that only users where their expiry date is greater than today's date to login.
protected function getCredentials(Request $request)
{
return ['email' => $request->{$this->loginUsername()}, 'password' => $request->password];
}
The code does not accept adding:
'expires' => gte(Carbon::now())
Any help is appreciated
I don't think this is possible, even in Laravel 5.5. Taking a look at the retrieveByCredentials method in Illuminate\Auth\EloquentUserProvider which is used to get the user from the database, you can see that the query passes simple key/value combinations to the where method on the $query object, which equate to where key = value. This is from 5.5:
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
return $query->first();
}
To achieve what you are after I would recommend doing this check after the user has logged in, in your controller for instance:
// Imagine this is the controller method where you're dealing with user logins
public function login(array $credentials)
{
if (! auth()->attempt($credentials)) {
// Handle what happens if the users credentials are incorrect.
}
$user = auth()->user();
if (Carbon::now()->gte($user->expires)) {
// User's account has expired, lets log them out.
auth()->logout();
// Return a redirect with a message or something...
}
// Handle a successful login.
}
I'm not sure if the auth() helper is available in 5.2, but you should be able to use the Auth facade to do the same thing, e.g. Auth::attempt(...).

Laravel 5 Authentication: Call to a member function getEmailForPasswordReset() on null

I'm using the authentication included with laravel 5.2
I have a problem at reset password form.
When I submit email, it returns this error:
Call to a member function getEmailForPasswordReset() on null
I found this cause by the following code:
$user->getEmailForPasswordReset()
the $user is null
In addition, I try to change Illuminate\Auth\Passwords\PasswordBroker at function emailResetLink
return $this->mailer->send($view, compact('token', 'user'), function ($m) use ($user, $token, $callback) {
$m->to($user->getEmailForPasswordReset());
if (! is_null($callback)) {
call_user_func($callback, $m, $user, $token);
}
});
I change: compact('token', 'user') -> ['token'=>$token, 'user2'=>$user]
And $user->getEmailForPasswordReset() -> $user2->getEmailForPasswordReset()
It works well!
Can you help me figure out what I did wrong?
Thanks.
The problem is definitely not in laravel files, so stop looking there and messing with the code, because you risk breaking more stuff than you will fix and also it will be overwritten once you do composer update.
The $user is null because the system cannot find the user you want to send a password reset link to. It would be more helpful to see your controller that implements the password resetting (submitting). Laravel comes with a pretty good starting point and you should not overengineer it unless needed: https://laravel.com/docs/5.2/authentication#resetting-routing
So the method for sending reset links would look something like this:
public function postEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$response = Password::sendResetLink($request->only('email'), function (Message $message) {
$message->subject($this->getEmailSubject());
});
switch ($response) {
case Password::RESET_LINK_SENT:
return redirect()->back()->with('message', 'Password reset link sent');
case Password::INVALID_USER:
return redirect()->back()->with('message', 'User not found');
}
}
As you see there's no User objects involved for you to handle.

Resources