I'm working on a login form which uses ajax and I can't setup login throttling.
ThrottlesLogins trait redirects somewhere but I don't need that. How can I return number of seconds when user fails password n times?
Controller:
<?php
namespace App\Http\Controllers\Login;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Auth;
class LoginController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
public function index()
{
if (Auth::check()) return redirect(url('/dashboard'));
else return view('admin.login');
}
public function login(Request $request) {
$this->validate($request, [
'username' => 'required', 'password' => 'required',
]);
$credentials = $request->only('username', 'password');
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
return $this->sendLockoutResponse($request);
}
if (Auth::attempt($credentials, $request->has('remember')))
{
return $this->handleUserWasAuthenticated($request, $throttles);
//return response()->json(['code' => 1, 'redirect' => url('/dashboard')]);
}
//else return response()->json(['code' => 0]);
if ($throttles) {
$this->incrementLoginAttempts($request);
}
}
}
routes.php
/* Login */
Route::group(array('prefix' => 'login', 'namespace' => 'Login', 'middleware' => 'guest'), function() {
Route::get('/', 'LoginController#index');
Route::post('/', 'LoginController#login');
});
You can change:
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
return $this->sendLockoutResponse($request);
}
into
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
if ($request->ajax()) {
return response()->json(['lockout_time' => $this->lockoutTime()]);
}
else {
return $this->sendLockoutResponse($request);
}
}
Related
When trying to integrate limit login attempts to custom controller in Laravel 6, I got the following error:
Error:
exception: "BadMethodCallException"
file: "D:\\wamp64\\www\\alphabilling\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Controller.php"
line: 68
message: "Method Modules\\Auth\\Http\\Controllers\\AuthController::username does not exist."
This is AuthController:
Code:
<?php
namespace Modules\Auth\Http\Controllers;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Auth;
use Validator;
use Illuminate\Foundation\Auth\ThrottlesLogins;
class AuthController extends Controller
{
use ThrottlesLogins;
protected $maxAttempts = 5;
protected $decayMinutes = 1;
public function login(Request $request)
{
$input = $request->all();
$rules = [
'username' => 'required|email',
'password' => 'required'
];
$validator = Validator::make($input, $rules);
if ($validator->fails()) {
return false;
}
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$authenticated = Auth::attempt([
'email' => $request->input('username'),
'password' => $request->input('password')
]);
if($authenticated){
$this->clearLoginAttempts($request);
} else {
$this->incrementLoginAttempts($request);
}
}
}
Version:
Laravel: 6.20.18
I am sending two different emails. One from VerifyMail and other from ForgetEmail but laravel throws error that VerifyMail does not exits. It was working just fine when i had not added the ForgetMail class.
This is my code where i am using VerifyMail
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\User;
use App\VerifyUser;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Carbon;
use App\Mail\VerifyMail;
class UserRegisterController extends Controller
{
public function __construct()
{
$this->middleware('guest:user');
}
public function showRegisterForm() {
return view('user.registerForm');
}
public function register(Request $request) {
$userValidated = $this->validate($request, [
'name' => 'required|string',
'email' => 'required|string|unique:users',
'password' => 'required|string|min:6|confirmed'
]);
$register = [
'name' => $userValidated['name'],
'email' => $userValidated['email'],
'password' => Hash::make($userValidated['password']),
];
$user =User::create($register);
VerifyUser::create([
'token' => str::random(60),
'user_id' => $user->id,
]);
Mail::to($user->email)->send(new VerifyMail($user));
return redirect('user/login')->with('email', 'An email was sent to you for verification');
}
public function verifyEmail($token, $date) {
$dates = date(strtotime($date));
if(time() - $dates > 60 * 60) {
return redirect('/user/login')->with('failed' , 'Oops! Something went wrong');
}
$verifiedUser = VerifyUser::where('token', $token)->first();
if(isset($verifiedUser)) {
$user = $verifiedUser->user;
if($user->email_verified_at == '') {
$user->email_verified_at = carbon::now();
$user->save();
return redirect('/user/login')->with('success', 'Your email was successfully verified');
} else {
return redirect('/user/login')->with('failed' , 'Oops! Something went wrong');
}
}
}
}
This is the code where i am using ForgetEmail
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
// use Symfony\Component\HttpFoundation\Session\Session;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Session;
use App\ForgetPassword;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Mail;
use App\Mail\ForgetEmail;
use App\User;
use Illuminate\Support\Facades\Hash;
class UserLoginController extends Controller
{
protected $guard;
use AuthenticatesUsers;
public function __construct()
{
$this->middleware('guest:user')->except('logout');
$this->guard = Auth::guard('user');
}
public function showLoginForm()
{
return view('user.login');
}
public function forgetPasswordForm() {
return view('user.forget');
}
public function passwordResetForm($id) {
$user = User::where('id', $id)->first();
return view('user.resetForm', ['id' => $id]);
}
public function userLogin(Request $request) {
$validated = $this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:6',
]);
if(Auth::guard('user')->attempt(['email' => $validated['email'], 'password' => $validated['password']], $request->remember)) {
$user = Auth::guard('user')->user();
if($user->email_verified_at != null) {
return redirect()->route('user.dashboard');
} else {
$this->logout($msg = 1);
}
}
return redirect()->back();
}
public function forgetPassword(Request $request) {
$email = $this->validate($request, [
'email' => 'required|email|exists:users',
]);
$forgetPass = User::where('email', $email['email'])->first();
$forgetArray = array(
'user_id' => $forgetPass->id,
'token' => str::random(60),
);
$forgetPassword = ForgetPassword::create($forgetArray);
Mail::to($email['email'])->send(new ForgetEmail($forgetPassword->user));
return redirect('user/forgetPassword')->with('email', 'We have sent you an email to reset your password');
}
public function passwordReset($token, $date) {
$checkTime = strtotime($date);
if(time() - $checkTime > 20 * 60) {
return redirect('user/login/')->with('passes', 'Looks like the link has expired');
}
$passReset = ForgetPassword::where('token', $token)->first();
if(isset($passReset)) {
$user = $passReset->user;
if($user->password) {
return redirect('/user/passwordReset'. '/'. $user->id);
}
}
}
public function passwordUpdate(Request $request, $id) {
$validatePass = $this->validate($request, [
'password' => 'required|string|min:6|confirmed',
]);
$pass = User::where('id', $id)->first();
$pass->password = Hash::make($validatePass['password']);
$pass->save();
return redirect('/user/login')->with('reset', 'Your password has been reset');
}
public function logout($msg = 0) {
$cookieName = $this->guard->getRecallerName();
$cookie = Cookie::forget($cookieName);
Auth::logout();
Session::flush();
if($msg == 0) {
return redirect()->route('user.login')->withCookie($cookie);
} else {
return redirect()->route('user.login')->withCookie($cookie)->with('verify', 'Your email is not verified');
}
}
}
All of my code was working fine before when i had not added that ForgetEmail class in Mail. I don't know what's the problem here. Will be glad if you can find it for me.
in My web application i have simple custom login and logout routes which logout route doesn't work correctly and after when i want to switch to this route, that redirect to home route as /:
Route:
Route::namespace('Auth')->prefix('page')->group(function () {
Route::get('login', 'LoginController#show')->name('login');
Route::post('login', 'LoginController#login');
// logout route when i use conteoller doesn't work
Route::get('logout', 'LoginController#logoutUser')->name('logout');
});
Layout:
<a href="{{ route('logout') }}" class="nav-link">
<i class="icon-switch2"></i>
<span>LOGOUT</span>
</a>
LoginController:
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo = '/';
public function __construct()
{
$this->middleware('guest')->except('logout');
}
public function login(Request $request)
{
$this->validateLogin($request);
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if (auth()->validate($request->only('password'))) {
$user = User::whereUsername($request->username)->first();
if ($user->active == 0) {
$checkActivationCode = $user->activationCode()->where('expire', '>=', Carbon::now())->latest()->first();
if ($checkActivationCode != null) {
if ($checkActivationCode->expire > Carbon::now()) {
$this->incrementLoginAttempts($request);
Session::flash('message', 'Please active your account');
return back();
}
}
}
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
public function logoutUser(Request $request)
{
dd($request->all());
//auth()->logout();
//$this->logout($request);
}
public function redirectToProvider()
{
return Socialite::driver('google')->redirect();
}
public function handleProviderCallback()
{
$socialUser = Socialite::driver('google')->stateless()->user();
$user = User::whereEmail($socialUser->getEmail())->first();
//dd($socialUser->getAvatar());
if (!$user) {
$data = [
'name' => $socialUser->getName(),
'email' => $socialUser->getEmail(),
'avatar' => str_replace('sz=50', 'sz=150', $socialUser->getAvatar()),
'mobileNumber' => '',
'loginType'=>'google',
'password' => bcrypt($socialUser->getId()),
];
//dd($data);
$user = User::create($data);
}
if ($user->active == 0) {
$user->update([
'active' => 1
]);
}
auth()->loginUsingId($user->id);
return redirect('/system/UserLoginWithGoogle');
}
public function show()
{
return view('auth.login');
}
protected function validateLogin(Request $request)
{
$this->validate($request, [
$this->username() => 'required|string',
'password' => 'required|string',
'g-recaptcha-response', 'recaptcha'
]);
}
}
I think you have error in your contructor. There is "->except('login')", but I think it's not related to route name, but to a method, so it needs to be "->except('logoutUser')". Let me know if it works.
When i try to Get the authenticated user data from API i get this error:
BadMethodCallException: Method App\Http\Controllers\UserController::getAuthenticatedUser does not exist. in file D:\OpenServer\OSPanel\domains\Pumpkin\vendor\laravel\framework\src\Illuminate\Routing\Controller.php on line 68
Please help to fix it
My userController:
namespace App\Http\Controllers;
use App\User;
use GuzzleHttp\Psr7\Response;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Validator;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Foundation\Auth\ResetsPasswords;
class UserController extends Controller
{
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
try {
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 400);
}
} catch (JWTException $e) {
return response()->json(['error' => 'could_not_create_token'], 500);
}
return response()->json(compact('token'));
}
public function logout()
{
auth()->logout();
return response()->json(['message'=>'Successfully logged out']);
}
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
if($validator->fails()){
return response()->json($validator->errors()->toJson(), 400);
}
$user = User::create([
'name' => $request->get('name'),
'email' => $request->get('email'),
'password' => Hash::make($request->get('password')),
]);
$token = JWTAuth::fromUser($user);
return response()->json(compact('user','token'),201);
}
public function getAuthenticatedUser()
{
try {
if (! $user = JWTAuth::parseToken()->authenticate()) {
return response()->json(['user_not_found'], 404);
}
} catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return response()->json(['token_expired'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['token_invalid'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['token_absent'], $e->getStatusCode());
}
return response()->json(compact('user'));
}
}
My api.php
Route::post('auth/register', 'UserController#register');
Route::post('auth/login', 'UserController#authenticate');
Route::post('auth/recover', 'UserController#recover');
Route::get('open', 'DataController#open');
Route::post('/password/email', 'ForgotPasswordController#sendResetLinkEmail');
Route::post('/password/reset', 'ResetPasswordController#reset');
Route::group(['middleware' => ['jwt.verify']], function() {
Route::get('user', 'UserController#getAuthenticatedUser');
Route::get('closed', 'DataController#closed');
});
Middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use JWTAuth;
use Exception;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
class JwtMiddleware extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try {
$user = JWTAuth::parseToken()->authenticate();
} catch (Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
return response()->json(['status' => 'Token is Invalid']);
}else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
return response()->json(['status' => 'Token is Expired']);
}else{
return response()->json(['status' => 'Authorization Token not found']);
}
}
return $next($request);
}
}
Can you provide the full log output from \storage\logs?
Also, sending the router as a parameter to the function may help.
Route::group([
'middleware' => 'jwt.verify'
], function ($router) {
Route::get('user', 'UserController#getAuthenticatedUser');
Route::get('closed', 'DataController#closed');
});
I just wanted to say if the user is not active, don't allow to login. I have made the controller as below, I am not sure what I am missing or what else I have to do here to make this work!
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Auth\Authenticatable;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class AuthController extends Controller{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
protected $redirectTo = '/home';
public function __construct()
{
$this->middleware($this->guestMiddleware(), ['except' => 'logout']);
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
]);
}
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
}
My thinking was authenticate() method should do the trick!
The below code worked for my case:
protected function getCredentials(Request $request)
{
return [
'email' => $request->input('email'),
'password' => $request->input('password'),
'active' => true
];
}
for Laravel 5.3 need to add following code to LoginController
protected function credentials(Request $request)
{
return [
'email' => $request->input('email'),
'password' => $request->input('password'),
'active' => true
];
}
i think you should create method to check if user passed your credentials, here's my suggestion :
protected function getCredentials(Request $request)
{
return [
'username' => $request->input('email'),
'password' => $request->input('password'),
'active' => true
];
}
and your login method:
public function login(Request $request) {
$this->validate($request,['email' => 'required|email','password' => 'required']);
if (Auth::guard()->attempt($this->getCredentials($request))){
//authentication passed
}
return redirect()->back();
}
hope you get basic idea.
In LoginController.php file write this function
protected function credentials(Request $request) {
$extraFields = [
'user_type'=> 'customer',
'user_entry_status' => 1
];
return array_merge($request->only($this->username(), 'password'), $extraFields);
}
Go to this path :
your-project-folder/vendor/laravel/framework/src/illuminate/Foundation/Auth/AuthenticatesUsers.php
$credentials=$request->only($this->loginUsername(), 'password');
$credentials['status'] = '1';
return $credentials;
Change getCredantials works fine, but it is good practice to let user know, that the account was suspended (credentials are OK, but the account status is not). You can easily override login method in Auth/LoginController.php to your own copy, add your own logic to login process and raise own exception.
in Auth/LoginController.php create login and sendAccountBlocked function
/*load additional classes to LoginController.php*/
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Auth;
public function login(Request $request){
//
$this->validateLogin($request);
//
// 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 ($this->attemptLogin($request)) {
//check user status
if (Auth::user()->user_status == 'A') return $this->sendLoginResponse($request);
// if user_status != 'A' raise exception
else {
$this->guard()->logout();
return $this->sendAccountBlocked($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);
//
}//
protected function sendAccountBlocked(Request $request){
throw ValidationException::withMessages([
$this->username() => ['Your account was suspended.'],
]);
}