I use the default email authentication in Laravel 5.7.3 (just updated from 5.4) with a sqlite DB. At login my email address gets validated case sensitive which is not what I want. It should be allowed to login with "JohnDoe#foobar.com" as well as "johndoe#foobar.com".
I've tried to add an accessor at my user model to always lowercase the database value.
public function getEmailAttribute($value) {
return strtolower($value);
}
But this one doesn't seem to be used by Auth at all. Additionally I don't know where to change the user input in the incomming request to lower case.
Is there a simple config case sensitive switch? Or where do I need to change/add scripts?
Thanks for your support
Daniel
Your accessor should be fine, but you should make sure that you also lowercase the given value, e.g. In your controller:
Assuming that you're using the default LoginController shipped from Laravel:
I overwrote the credentials method from AuthenticatesUsers in App\Http\Controllers\Auth\LoginController
protected function credentials(Request $request)
{
$credentials = [
$this->username() => strtolower($request->get($this->username())),
"password" => $request->get("password")
];
return $credentials;
}
This works fine, when all emails stored in the database are already stored all-lowercase. If you're starting from scratch you can enforce the emails to be stored lowercase by default. Just implement the following mutator in your App\User:
public function setEmailAttribute($value)
{
$this->attributes['email'] = strtolower($value);
}
If you have stored mixed-case email already, you can update all emails to lowercase emails using this query:
UPDATE users SET email = LOWER(email);
But this still feels kind of incomplete and you maybe don't want to manipulate your data this way. I am pretty much sure that there are more possibilities to make this happen but unfortunately I am not able to dig into this further for now. However my next attempt would be to extend/write a custom UserProvider.
You have to call getEmailAttribute(/your email here/)
before login and signup like this
$request->email = getEmailAttribute($request->get('email'));
Related
I am using request validation as
php artisan make:request ClientRequest
As you can see, on client edit form if password field is not empty I am able to use $request->validated() method on database update,
However if password field empty(user dont want to change password),
I am not able to use $request->except('password')->validated() method.
I use $request->except() method due to this situation.
Does this pose a security problem?
public function update(ClientRequest $request, Client $client)
{
$validated = $request->validated();
if($request->filled('password') )
{
Client::whereId($client->id)->update($validated);
}else{
Client::whereId($client->id)->update($request->except('password'));
}
return redirect('/clients')->with('success', 'success');
}
Client::whereId($client->id)->update($request->except('password'));
That line is does pose a big security problem especially if you are relying on validation to set fields rather than the fillable attribute. $request->except('password') will return all the other fields that the user submitted so if the user had added something like is_admin => true in the request, you'll end up setting it on the db if it exists.
You can use \Illuminate\Support\Arr::except() on the validated data to make sure that you are only getting the data you expect. That would change the that particular line to
Client::whereId($client->id)->update(\Illuminate\Support\Arr::except($request->validated(), 'password'));
PS: You already have the client through route model binding so you don't need to query it you can update that client directly i.e
$client->update(\Illuminate\Support\Arr::except($request->validated(), 'password'));
You are validating all fields sent to update() in both scenarios.
You would have had an issue if you sent the password field in both cases, but only validated it in one of them. That's not the case.
So looks fine to me from that perspective.
I want to hide the login form and display an error message instead, but I can't.
I tried to put the code below that rewrites the action on the controller that shows the form, but the method that checks for too many login attempts doesn't seem to work and never returns true.
public function showLoginForm(Request $request)
{
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request) ) {
$seconds = $this->limiter()->availableIn($this->throttleKey($request));
return view('auth.block', array(
'seconds' => $seconds
));
}
return view('auth.login');
}
I managed the authentication process with php artisan make: auth login controller is the default generated by Laravel, the only change is in the action that displays the form.
The function hasTooManyLoginAttempts() needs, in the $request, the username (usually the email) as a key to know if the user has reached his max login attempts.
If, in the $request, there is not the username with a value the function is unable to verify the user login attempts.
So you cannot really know who is the user that wants to get your login form, you know who is only after he submitted the form.
IMHO the only way could be to add a username parameter to the GET request but you shoud provide it with some workarounds: cookies, session etc.
Looking at Laravel's code, it checks for hasTooManyLoginAttempts based on throttleKey and maxAttempts.
The throttleKey is dependent on the user's email and IP address. So the output of the following code is something like: info#example.com|127.0.0.1 and that is your throttleKey.
protected function throttleKey(Request $request)
{
return Str::lower($request->input($this->username())).'|'.$request->ip();
}
Now Laravel gets the user's email (username) from $request->input($this->username()) when you send a POST request, which you don't have access to in the showLoginForm method because it's called on the GET request.
Anyway, if you want to block the login form you'll need to come up with your own unique throttleKey and then override the method. Say you want your throttleKey to be based only on the IP address - which is not recommended. Here's how you do it:
// In LoginController.php
protected function throttleKey(Request $request)
{
return $request->ip();
}
I'm developing a website using Laravel with an existing database.
My User table doesn't have a remember me field. But I want users always login with remember me feature. Can anyone help?
"Remember me" functionality NEEDS a field in DB. Actually you have to store the "remember me" cookie somewhere in your program. Where is a better place than DB to do this??
First you have to add remember_token (100-character string) to your Users table in database which will be used to store the "remember me" token.
In the next step, On your login attemp you must add $remember parameter like this:
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// The user is being remembered...
}
after this code, your user will be remembered by his remember cookie. SO he can be remembered like this:
if (Auth::viaRemember()) {
//
}
Anyway, if you want to store the remember_token in another table, you have define a model like RememberToken and add this relationship to your Users model:
public function rememberToken() {
return $this->hasOne('RememberToken'); // also add foreign keys
}
And now some overriding is needed!. First override getRememberToken() and setRememberToken() methods and also you need to override getRememberTokenName().
As a result EloquentUserProvider::retrieveByToken() should be changed in EloquentUserProvider class. You probably have to make your User model to join remember_tokens table on every query by adding global scope to it, and return 'remember_tokens.token' from getRememberTokenName() method.
Is it really necessary by the way?
I've got an issue when I try to validate the post password via Hash::check in Laravel 5.5
I made a posts table (in this case sales table) with password column. When I try to create the post, it's working perfectly and the password is hashed and also belongs to logged in User. Then on the current post page is a button with an input (password) to delete that specific post, but the condition is always false.
My Controller public function destroy(Request $request, $id)
$input_pass = request('input_password');
$sale = Sale::find($id);
$hashed = $sale->password;
// Check if sale password is correct
if (Hash::check($input_pass, $sale->password)) {
$sale->delete();
} else {
// something else to do
}
For the post store, I used bcrypt method to hash the password. I've been also trying to dd('sale->password') which refers to column in sales table (correct) and dd('$input_pass') which refers to typed in password in DELETE form (also correct) - so I'm a little bit confused, why the pass don't match.
From your comment I find that you have a logical error where you initially hash your password and persist it in DB.
You are passing the string password to bcrypt where it should actually be something like request('password')
Change
'password' => bcrypt('password'),
to
'password' => bcrypt(request('password')),
I'm creating admin user via model and it saving record successfully but password is not being hashed as follows:
$request->password = bcrypt($request->input('password'));
Admin::create($request->except('_token'));
you can not modify $request properties like that.
Give it a try:
$input = $request->except('_token');
$input['password'] = bcrypt($input['password']);
Admin::create($input);
OR, handle it in your Admin Model
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
Then you can
Admin::create($request->except('_token'));
Take a look at Laravel's Hashing documentation. It shows that you should be hashing any strings like so:
Hash::make($request->newPassword)
However looking at your code, i'd say this issue is actually the fact you're trying to modify the request $request->password, this is not going to work how you expect. Look at your Admin model class and see what the code is expecting, perhaps this is already in built if you pass the correct arguments.