I need to do some extra checks on a user, I would like to get the user by username and password.
Firstly:
Is there a built in function that gets a user by username and password without authenticating them?
Secondly:
If the above is no, then how do I correctly hash the password, because if I use Hash::make( $password ) and then compare to the database, it is not the same... You would usually use Hash::check but I need to actually get the user by username and password.
In Laravel 5.2
You can use Auth::once($credentials) to validate credentials and thereafter Auth::getUser(); to get the user.
$credentials = Request::only('username', 'password');
if(!Auth::once($credentials)) {
// Invalid user credentials; throw Exception.
}
$user = Auth::getUser();
First:
If you want to check if user data to authentication is correct you can use:
if (Auth::validate($credentials))
{
//
}
But if you want to get user from database with user and password, you can use:
$user = User::whereName($username)->wherePassword(Hash::make($password))->first();
Second
To store password in database you should use Hash::make($password) as you showed and it works without any problems. Using Auth::validate should solve the issue.
Yes, there is a built in function you should use. I recommend you to read the docs. But here's a good example, it's pretty self-evident:
$input = array(
'username' => Input::get('username'),
'password' => Input::get('password'),
);
$remember = (boolean)Input::get('remember'); //'Remember me' checkbox
if (Auth::attempt($input, $remember)) {
return Redirect::intended('dashboard')->with("success", "You're logged in!"); //Redirect the user to the page intended to go to, with the dashboard page as default
}
Registering a user looks something like this:
$input = array(
'username' => Input::get('username'),
'email' => Input::get('email'),
'password' => Hash::make(Input::get('password')) //Encrypt password
);
$user = User::create($input);
I also recommend you to read about input validation. I hope this helps, good luck.
Edit: I didn't read the "without authenticating them" part. You should use Auth::validate($input) as Marcin already explained.
Laravel 5.7
To check a users credentials without logging them in, I had to do this:
$user = User::whereEmail($request->email)->first();
$user = password_verify($request->password, optional($user)->getAuthPassword()) ? $user : false;
Laravel auth validation makes use of https://www.php.net/manual/en/function.password-verify.php
Related
Just want to be sure if that's the "right way" and most importantly if it's "secure way". I'm making simple form for someone who want's to reset password (not logged in). I don't want to use Laravel way (no dedicated database for that). Simply I'm sending crypted link, link is valid for 10 minutes.
public function sendConfirmationLink(Request $request)
{
$validatedData = $request->validate([
'email' => 'required|email|exists:users',
]);
$user = User::where('email', $request->get('email'))->firstOrFail();
$parameters = [
'user_id' => $user->id,
'date' => Carbon::now(),
'type' => 'password_reset',
];
$passwordResetLink = Crypt::encrypt($parameters);
SendPasswordResetLink::dispatch($user, $passwordResetLink)->onQueue('high');
}
Later on, I'm decrypting that hash and I'm making password change.
public function resetPassword(Request $request)
{
$validatedData = $request->validate([
'hash' => 'required',
'new_password' => 'required|string|min:6|confirmed',
]);
try {
$decrypted = Crypt::decrypt($request->get('hash'));
$password = Hash::make($request->get('new_password'));
$user = User::where('id', $decrypted['user_id'])->firstOrFail();
$user->password = $password;
$user->save();
} catch (DecryptException $e) {
abort(404);
}
}
If I'm right... that "link" will be extremely difficult to crack and it's valid for only 10 minutes. Is it in any way less secure then native Laravel version with similar code stored in database?
Yes I know that Laravel Auth can do it for me. I want use my method in more then password reset so I'm looking for answear if it's secure this way.
The Crypt library from Laravel uses a combination of OpenSSL and AES-256-CBC with a MAC signing. I think for a 10 min period, that should be safe enough.
A more serious problem is, that you can't "disable" a link after usage. So theoretically everyone with the link can change the account password infinite times (in a 10 min period).
A solution for that problem could be, that you add the current password hash into your link-hash and then compare that. In that case the password change would only work one-time per link.
I created a user with encrypted password.(bcrypt(password))
In rest api I am getting md5(password) [from app]
How to validate both the password?
\Hash::check($request->password, $user->password)
NOT WORKING
There is no direct way to compare the actual password in Laravel. Laravel never store your password as plain text but a hashed version + salt, so you can check the plain version of the password against the hashed stored version using the Hash::check method:
// original password
$password = 'my-password';
// hashed password
$hashed_password = bcrypt($password);
// something like: "$2y$10$XFs6ocWUaiiB99QvLwTuhOuABIq71D13LmpFdeISh7RsC.SsAthHG";
The hashed version is the one stored.
If you want to check if the validation of a password:
use Illuminate\Support\Facades\Hash;
// ...
$passed = Hash::check($password , $hashed_password); // true
You can not compare passwords encrypted by different ways. You need the plain password. I guess you are trying to make a seamless access for users in your platform in order to avoid the user do two times login (in your system and the api provider system) Am I correct?
In that case, probably you need to think in other way to approach it.
In Laravel 9 i make like this,
first import
use Illuminate\Support\Facades\Hash;
in rules() function
return [
'password' => ['required', function ($attr, $value, $fail) {
$user = User::where('email', $this->request->get('email'))->first();
if ($user && !(Hash::check($value, $user->password))) {
$fail('The ' . $attr . ' is invalid.');
}
}],
];
My problem is that I'm using CodeIgniter. I created an Administrator Panel which I use to create users and passwords. I use password_hash before sending them to database (in fact, in the database I can see the hashed password).
The problem is... When I try to log in, it doesn't. I can only access with administrator account, which I created before I started using CodeIgniter.
I can log in with admin account, but not with the accounts created with the admin panel.
I tried to use a very simple php file (outside CI) to copy and paste the hash to one of the user's fields in my database and it worked. I don't understand why this doesn't work within CodeIgniter.
Here's the part of my code:
public function insert_user($username, $passw, $perm){
$hashed_pass = password_hash($passw, PASSWORD_DEFAULT);
$data = array(
"username" => $username,
"passw" => $hashed_pass,
"perms" => $perm
);
$this->db->insert('usuarios', $datos);
}
The function above inserts correctly data in the database. I can see the password hashed, but when I try to log in with created users from admin panel, it just doesn't work (but it does with admin account, and they all are using the same function to log in).
public function login($username, $password){
$query = $this->db->query("select passw, perms from users where username = '".$username."'");
$result = $query->row();
$p_hashed = $result->passw;
$perms= $result->perms;
if(password_verify($password, $p_hashed)){
$info= array(
"is_valid" => true,
"username" => $username,
"perms" => $perms
);
return $info;
}
else {
$info= array(
"is_valid" => false,
"username" => ""
);
return $info;
}
}
I checked database and CI charset, all is utf8. I don't understand why is not working... I would appreciate any help.
P.S.: if the name of variables are no correlated or are not the same is because I translated it to English (I'm not native English speaker, so I'm not using English in this project).
Thank you.
EDIT
I've changed the code to use md5 instead of password_hash and I'm having the same issue.
EDIT 2
I detected the problem: I was comparing the 2 password (the first one and the confirm password). After that comparison, I passed the data to the model... But the variable of the password I was sending to model was a new variable with no data. I was hashing a not initialized variable.
There doesn't seem to be any problem in the code you show. As I commented, the likely problem is that the passw column in the table is truncating the data being inserted.
Check the documentation for password_hash to find what the return data size is for the various algorithms and adjust your table structure accordingly. For PASSWORD_DEFAULT the suggested size is 255 characters.
The code below isn't an answer, but I have time and thought you might find a refactored version of login() interesting.
It is assumed that you have properly validated and sanitized the values being passed to the login() method.
public function login($username, $password)
{
//use query binding so values are automatically escaped, producing a safer query
$query = $this->db->query("select username, perms, passw
from users where username = ?", [$username]);
// make sure query worked and found a record
if($query && $query->num_rows() > 0)
{
// get an row array so you don't need to make an array
$row = $query->row_array();
if(password_verify($password, $row['passw']))
{
unset($row['passw']); // remove passw from array
$row['is_valid'] = true;
return $row;
}
}
return array( "is_valid" => false, "username" => "");
}
To create a user without the end-user having to type in the details.
Something akin to
User::classcreate([
'email' => $email,
'password' => $rand_pass,
.....
]);
Thanks for the ideas and feedback in advance :)
The use case is.
The end-user invites another user to use the service by typing in their email and it creates a user with a random password before sending a email to the new user with their created details.
You're almost there with your code. It should look like this:
User::create([
'email' => $email,
'password' => bcrypt($rand_pass),
]);
But if you want to hash the password, you should also send the password to that user through email (which is not very secure). When the users logs in for the first time, you should at least require him to change the password.
You can simply use create() method of your User model:
$userData = [
'email' => 'some#email.com',
'password' => 'somepassword'
];
$newUser = User::create($userData);
You'll also need your password hashed in order for it to work with Laravel's authorization. Add the following to your user model - it will hash password before it's saved to the database:
public function setPasswordAttribute($password)
{
$this->attributes['password'] = Hash::make($password);
}
Hi i'm new to laravel and was trying to use the auth system.
the problem i'm having is that when verifying the users username and password by auth::attemp() I end up with a 'whoops something went wrong page' but only when there is a known username being specified. When i'm inputting an unkown username i get the expected error page.
i've created an user by:
// add user
$user = new User;
$user->username = 'John';
$user->password = Hash::make('Doe');
$user->email = 'info#mail.com';
$user->save();
the route i've created for the post method looks like this:
Route::post('login', function(){
$credentials = array(
'username' => Input::get('username'),
'password' => Input::get('password')
);
// check credentials
if (Auth::attempt($credentials)) {
// go to index if login is successful
return Redirect::to('/');
}
// fail
return Redirect::to('login')->with('error' , 'Wrong username or password');
});
Now as i've said the error message is being shown if I specify an username wich is not (in this case) 'John'. when is do use 'John' I end up with an error no matter the password is correct or incorrect.
my question: What am i doing wrong here? and how to fix it?
It turned out to be a really stupid mistake. I edited the User model (app/models/User.php), this was causing the problem. I've reverted back to the original and everything works fine now!
Thanks for the help guys!