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

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

Related

Get Auth User ID Laravel

I Made Laravel Project And install the Breeze package for multi authentication And the Create a guard call admin in order to control user assess to dashboard It works fine Here is the route
Route::get('/dashbord',[AdminController::class, 'Dashbord'])
->name('admin.dashbord')
->middleware('Admin');
Route::get('/profile/edit',[AdminProfileSettings::class, 'index'])
->name('admin.profile.settings')
->middleware('Admin');
Here Is the middleware
public function handle(Request $request, Closure $next)
{
if(!Auth::guard('admin')->check()) {
return redirect()->route('login_form')->with('error','please Login First');
}
return $next($request);
}
This code works fine but the problem is when I log in to the dashboard and try to get admin ID to admin.profile.settings route it wont get the Id, I Passed the logged admin id by using AdminProfileSettings controller like this
public function index()
{
$id=Auth::user()->id;
$adminData = Admin::find($id);
return view('admin.admin_profile_settings',compact('adminData'));
}
But, when I try to access it in the admin.admin_profile_settings view it show me this error:
Trying to get property 'id' of non-object
But, if I use $adminData = Admin::find(1); it get the Id without any problem but when I try to get auth user id it show me the error and if I have logged in using default guard this error wont show but it get the id from users table
You're not using the auth:admin middleware, so the Auth facade is going to pull the user from the default guard defined in the config (which is web, unless you've changed it).
Without using the auth:admin middleware, you'll need to specify the guard for which to get the user.
$adminUser = Auth::guard('admin')->user();
Note 1: if you have the $request variable, you can also pull the user off of the $request with $request->user(), instead of reaching out to the Auth facade. It's just a matter of preference. The user() method also takes a guard as a parameter, if needed.
$adminUser = $request->user('admin');
Note 2: the user() method (Auth and request) returns the fully hydrated model. There is no need to get the id and re-retrieve the model.

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'));

New hashing of password - redirect users to password reset on login attempt

Here is an "update" In the previous version of my project we didn't have any proper hashing on password. So I want to use Laravel's hashing, and invite the users to make a new password.
What I have is a new password column in my User table. If when the user tries to log in, the new password doesn't exist (empty column), we automatically do a "reset password." I would like to know where to do this verification:
class LoginController extends Controller
{
public function login(Request $request)
{
//check if the user has an empty password
//if yes
redirect('/password/reset');
//else
//use normal login function
}
}
Is that the correct place? And do I need to rewrite all login content in the "else" ? (sorry this is a basic question)
I suggest that you create a middleware ( EnsurePasswordIsAdded as an example ) for your case and not include the verification process in a controller, because a controller usually contains functions that interact either a database or an external API to provide a response to the user which is not the case for you, you're just filtering/verifying the request.
here's the documentation link about middlewares in Laravel:
https://laravel.com/docs/5.8/middleware
here's a code suggestion:
public function handle($request, Closure $next)
{
if ( !User::find($request->email)->hasPassword() ) {
return redirect('password-reset')->with('email',$request->email);
}
return $next($request);
}

Laravel5.2 default auth check if user is verified in database before login and display custom message

I am using laravel5.2 auth and I have added an additional field : verified(y/n) in users table. Now before login I want to check if user is verified(Y) in database and if not verified display a message that your account is not yet verified.
If method authenticated exists in the AuthController it will be called from Laravel trait AuthenticatesUser. Use this method to block the user if it is not verified yet and, if required, resend the email.
public function authenticated(Request $request, $user)
{
if (!$user->activated) {
$this->activationService->sendActivationMail($user);
auth()->logout();
return back()->with('warning', 'You need to verify your account. We have sent you an activation code, please check your email.');
}
return redirect()->intended($this->redirectPath());
}
In /vendor/laravel/framework/src/Illuminate/Foundation/Auth you will find AuthenticatesAndRegistersUsers.php there you define what you want to do with your login.

How to forward argument for redirect::route->with when hitting an auth filter using Laravel?

I have the following problem:
After a user is created and the user has confirmed her email.
I would like the user to login,
I use the following code to do that:
Redirect::route('login-forward')->with('alert_message', ...)
the alert messages simply states that everything went well, and I would like her to login.
login-forward is protected with an before filter, that runs the actual login form,
When the user then logs in sucesfully, she is brought back to login-forward, whic then puts the user at her personal landing page.
the code for the route 'login-forward is:
Route::get('my-login', array(
'as' => 'login-forward',
'before' => 'auth',
function ()
{
$user = Auth::user();
switch ($user->role)
{
case 'Administrator':
return Redirect::route('admin_dashboard');
case 'FreeUser':
return Redirect::route('cs-dashboard');
default:
return Redirect::route('/');
}}));
the problem is, the ->with('alert_message',...) is not seen by the real login route called by the before filter.
How do I solve this?
The best approach is to let the user logs in automatically when the email is confirmed, if the user confirms the account creation then when you find that user is valid then you may log in that user using something like:
// $user = Get the user object
Auth::login($user);
Also you may use:
Session::put('alert_message', ...);
Redirect::route('login-forward');
Then when the user logs in for the first time, just get the message from Session using:
// Get and show the alert_message
// from session and forget it
#if (Session::has('alert_message'))
{{ Session::pull('alert_message') }}
#endif
So, when you pull the message from the Session to show it, the message will no longer be available in the Session or you may use Session::reflash() to flash the flashed session data for another subsequent request until you get in the page where you want to show the message.
The best choice is - you can make forward to login form without redirect from method of controller for url of personal page.
return Route::dispatch(Request::create('login'))->getOriginalContent();

Resources