How to implement maximum login attempts while not using default LoginController? - laravel

I want to prevent user login more than let's say 3 times. I know that there's a trait ThrottlesLogins. I also know that I can set everything in hasTooManyLoginAttempts. But what if my app works differently and I did not created auth via make:auth command (I don't have LoginController). How should I use this hasTooManyLoginAttempts method?
I've tried to add the following into my login method:
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}

yah you can do it , keep a field login_attempt in your table .
and check the count if he fails upadate the field 1. and then check his
login attempt.
$attempt = User::where('id',$userid)->first();
if($attempt->login_attempt == 3){
//write your code for inactive user or block the user
}else{
//redirect to dashboard
}

Related

Auth facade doesn't work without Sanctum api as middleware in Laravel 8

I'm creating an api through which anybody can view a page, however only admin can see all posts, while users are restricted to approved only. This is implemented via is_verified boolean variable where admin is given value of 1 and user the value of 0. I want to create a function like this
public function show(){
if(Auth::check()){
$showAllDetails = Events::all();
echo $showAllDetails;
}else {
$showUserDetails = Events:all()->where('is_verified',1);
echo $showUserDetails;
}
}
However Auth:check only works if I have sanctum api in my route
Route::middleware('auth:sanctum')->group(function () {
Route::get('view', [ViewController::class, 'show']);
});
If I run this code on Hoppscotch, it only shows if the admin is logged in (User don't require login). So a user can't see any post. If I remove the auth:sanctum middleware, only the else part of the code runs and no auth check or any stuff can run .
I need a way to incorporate both in a single function so that I can create a single route instead of creating two routes for different persons. Any way of doing such things?
public function show(){
if(Auth::check()){
$showAllDetails = Events::all();
echo $showAllDetails;
}else {
$showUserDetails = Events::where('is_verified',1)->get();
echo $showUserDetails;
}
}
I guess your else part is incorrect query, change your else part like above

Laravel stuck on email/verify

I just applied the laravel email-verification and wanted to make sure my users are verified, before entering page behind the login.
I added the follwing code:
class User extends Authenticatable implements MustVerifyEmail
...
Auth::routes(['verify' => true]);
...
Route::get('management', function () {
// Only verified users may enter...
})->middleware('verified');
If a user registers he gets a note and an email to verify his mail. He clicks the button in the mail, gets verified and everything works perfectly well.
But I discovered another case:
If the user registers and won't verify his mail, he will always get redirected to email/verify.
For example if accidentally having entered a wrong email, he can't even visit the register page, because even on mypage.com/register he gets redirected to mypage.com/email/verify!
Is this done on purpose by Laravel? Did I miss something? Do I have to / is it possible to exclude the login/register pages from verification?
Thank you in advance
I have this issue before, I have this way to resolve that, if you want to customize it you can consider this way.
In LoginController.php you can add this a little bit code, I overwriting the default login method:
public function login(Request $request)
{
$this->validateLogin($request);
$user = User::where($this->username(), $request->{$this->username()})->first();
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($user->hasVerifiedEmail()) {
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
})
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
You can overwrite and add a new parameter to the sendFailedLoginResponse too to let the method know when to redirect to email/verify page or just add else in $user->hasVerifiedEmail() if block to redirect him to email/verify page
EDIT:
You can delete $this->middleware('guest') in LoginController and RegisterController to make logged in user can go to register and login page, but it will be weird if someone who already logged in can login or register again.
I had the same problem and I solved it very user friendly... (I think!)
First: Inside View/Auth/verify.blade.php put a link to the new route that will clear the cookie:
My mail was wrong, I want to try another one
Second: On your routes/web.php file add a route that will clear the session cookie:
// Clear session exception
Route::get('/clear-session', function(){
Cookie::queue(Cookie::forget(strtolower(config('app.name')) . '_session'));
return redirect('/');
});
This will clear the cookie if the user press the button, and redirect to home page.
If this doesn't work, just make sure that the cookie name you are trying to forget is correct. (Use your chrome console to inspect: Application -> cookies)
For example:
Cookie::queue(Cookie::forget('myapp_session'));

How to hide login form after reaching the total of failed login attempts?

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();
}

Laravel 5.5 restrict duplicate login

I have overwritten Login and Logout functionality as I need to check many more conditions to authenticate the user like below.
public function login(Request $request)
{
$this->validateLogin($request);
$input=$request->all();
$user=User::where('username',$input['username'])->first();
//If Temp Password is set
if(strlen($user->temp_password)>10)
{
if (Hash::check($input['password'], $user->temp_password))
{
Auth::login($user);
$this->setUserSession($user);
$landing_page=Menu::find($user->landing_page);
return redirect()->route($landing_page->href);
}
else {
session()->put('failure','Invalid Username or Password');
return redirect('/login');
}
}
else{ //If Temp password is not set
if (Hash::check($input['password'], $user->password))
{
Auth::login($user);
$this->setUserSession($user);
$landing_page=Menu::find($user->landing_page);
return redirect()->route($landing_page->href);
}
else {
session()->put('failure','Invalid Username or Password');
return redirect('/login');
}
}
}
Now I need to restrict Same user from login once again in some other screen or place. I have checked Session Data but nothing is stored as Unique for a User.
ie. If a username admin is loged in US the same username admin must not be allowed to login from UK.
Update
Oh bagga, question wasn't quite clear. You are trying to restrict the number of sessions to 1 only. If I get it, then you will have to use a database session driver. Right now, I think you may be using the default driver (file). It only checks the session within the same browser. Using database session may allow you to check for session everywhere, and restrict the number of connections.
First, make sure your routes are within the web middleware so they can access sessions. Then, inside of the web middleware, create a group of routes that are only accessible for users who are not logged in.
Route::group(['middleware' => 'guest'], function () {
Route::get('login', 'LoginController#login');
// any other route
});
Logged in users won't be able to access the login route anymore.
You could also do the check in your login function to see if the user's is already connected by using
if (Auth::check()) {
// user is connected
// redirect them
}
What does this->setUserSession($user) do?
You can do this using login token.
Generate a login token and keep it in database.
And check for it's entry in database while logging in.
If it doesn't exist let log in success.
Else fail.
And delete login token every time user logs out.
Or
you can generate new token on each login success. And deleting old token and invalidating the old login.
But in this case you have to keep that token in session and for each request you have to check that token with database token.
If it matches, allow user
Else logout the user with notice.
I'll prefer the second method personally.
As you can check for the token in the middleware itself.

How can I logout user after delete with Laravel?

What is the correct way to logout user after I delete his data in Laravel? I would not like to delete him before, in case of delete process goes with errors.
When I am having this code:
if($this->userManipulator->softDeleteUser(Auth::user())){
Auth::logout();
return redirect(url('login'));
}
it works fine in the app, but does not work correctly during testing.
As I mentioned in the comments, you must log the user out of your application first since once deleted Eloquent won't be able to locate/logout the user.
Below is a solution that addresses your concern about what to do if the delete fails. It might need adjustment depending on how you have things setup, but this concept will work:
// Get the user
$user = Auth::user();
// Log the user out
Auth::logout();
// Delete the user (note that softDeleteUser() should return a boolean for below)
$deleted = $this->userManipulator->softDeleteUser($user);
if ($deleted) {
// User was deleted successfully, redirect to login
return redirect(url('login'));
} else {
// User was NOT deleted successfully, so log them back into your application! Could also use: Auth::loginUsingId($user->id);
Auth::login($user);
// Redirect them back with some data letting them know it failed (or handle however you need depending on your setup)
return back()->with('status', 'Failed to delete your profile');
}
This is not possible, Auth won't be able to located them because Eloquent treats them as deleted.
Solution: You should logout user before delete.
$user = \User::find(Auth::user()->id);
Auth::logout();
if ($user->delete()) {
return Redirect::route('home');
}

Resources