authService->getRedirectRouteForUser(Auth::user())); } return view('login'); } public function login(Request $request) { // Custom validation check: treat all validation errors as "Invalid email/password" $validator = \Illuminate\Support\Facades\Validator::make($request->all(), [ 'mail' => 'required|email|max:255', 'password' => 'required|string|max:100', ]); if ($validator->fails()) { return back()->withInput($request->only('mail'))->withErrors([ 'mail' => 'Thông tin đăng nhập không đúng. Vui lòng nhập lại', ]); } $credentials = $validator->validated(); $email = \Illuminate\Support\Str::lower($credentials['mail']); $throttleKey = 'login-attempts:' . $email; // Brute-force check (lock after 5 failed attempts) if (\Illuminate\Support\Facades\RateLimiter::tooManyAttempts($throttleKey, 5)) { Log::warning('Brute-force login attempt blocked', ['email' => $email, 'ip' => $request->ip()]); return back()->withInput($request->only('mail'))->with('dialog_error_brute_force', 'Tài khoản của bạn tạm thời bị khóa do nhập sai mật khẩu quá 5 lần. Vui lòng thử lại sau 15 phút.'); } $result = $this->authService->attemptLogin($credentials, $request); if (!$result['success']) { if ($result['error_key'] === 'permission') { Log::warning('Login attempt by deactivated user', ['email' => $email, 'ip' => $request->ip()]); return back()->withInput($request->only('mail'))->with('dialog_error_permission', $result['error_msg']); } // For invalid password / email (counts towards brute-force attempts) \Illuminate\Support\Facades\RateLimiter::hit($throttleKey, 900); // 15 minutes lockout Log::warning('Failed login attempt', ['email' => $email, 'ip' => $request->ip()]); return back()->withInput($request->only('mail'))->withErrors([ 'mail' => $result['error_msg'], ]); } // Clear rate limiter on success \Illuminate\Support\Facades\RateLimiter::clear($throttleKey); $user = $result['user']; Log::info('User logged in successfully', ['user_id' => $user->id, 'email' => $user->mail, 'ip' => $request->ip()]); if ($user->role != config('constants.ROLE_ADMIN') && $user->first_login == config('constants.FIRST_LOGIN_TRUE')) { return redirect()->route('login')->with('dialog_first_login', true); } return redirect($this->authService->getRedirectRouteForUser($user)); } public function logout(Request $request) { $userId = Auth::id(); $this->authService->logout($request); Log::info('User logged out', ['user_id' => $userId, 'ip' => $request->ip()]); return redirect('/login'); } public function forgotPassword(Request $request) { $validator = \Illuminate\Support\Facades\Validator::make($request->all(), [ 'mail' => 'required|email|max:255', ], [ 'mail.required' => config('constants.MSG_FORGOT_PWD_EMAIL_REQUIRED'), 'mail.email' => config('constants.MSG_FORGOT_PWD_EMAIL_INVALID'), ]); if ($validator->fails()) { return back()->withInput()->with('dialog_forgot_error', $validator->errors()->first()); } $result = $this->authService->forgotPassword($request->input('mail')); if (!$result['success']) { Log::warning('Forgot password requested for non-existent/invalid email', ['email' => $request->input('mail'), 'ip' => $request->ip()]); return back()->withInput()->with('dialog_forgot_error', $result['error_msg']); } Log::info('Forgot password requested successfully', ['email' => $request->input('mail'), 'ip' => $request->ip()]); return back()->with('dialog_forgot_success', $result['message']); } }