validate([ 'username' => 'required|string', 'password' => 'required|string', 'remember' => 'sometimes|boolean', ]); $throttleKey = 'admin-login:' . strtolower($request->string('username')->toString()) . '|' . $request->ip(); if (RateLimiter::tooManyAttempts($throttleKey, self::LOGIN_MAX_ATTEMPTS)) { return response()->json([ 'error' => true, 'message' => 'Bạn đã đăng nhập sai quá nhiều lần. Vui lòng thử lại sau ít phút.', ], 429); } $guard = Auth::guard('admin'); $remember = $request->boolean('remember'); $credentials = $request->only('username', 'password'); $token = $guard->attempt($credentials); if (!$token) { RateLimiter::hit($throttleKey, self::LOGIN_DECAY_SECONDS); $this->activityLogger->log( event: 'login_failed', logName: 'auth', description: '[admin] Đăng nhập thất bại với username: ' . $request->username, ); return response()->json([ 'error' => true, 'message' => 'Thông tin đăng nhập không chính xác.', ], 401); } RateLimiter::clear($throttleKey); $user = Auth::guard('admin')->user(); Auth::guard('admin')->getProvider()->rehashPasswordIfRequired($user, $request->only('password')); $this->activityLogger->logAuth('login', 'admin', $user->id, $user->username ?? $user->email); $data = [ 'token' => $token, 'token_type' => 'bearer', 'expires_in' => config('jwt.ttl') * 60, 'user' => new AdminUserResource($user), ]; if ($remember) { $refreshToken = $this->refreshTokens->create($user->id, now()->addDays(30)); $data['refresh_expires_in'] = self::REMEMBER_TTL_SECONDS; } $response = response()->json([ 'error' => false, 'data' => $data, ]); return $remember ? $response->withCookie($this->refreshCookie($refreshToken, self::REMEMBER_TTL_SECONDS)) : $response->withCookie($this->forgetRefreshCookie()); } public function me(): JsonResponse { $user = Auth::guard('admin')->user(); return response()->json([ 'error' => false, 'data' => ['user' => new AdminUserResource($user)], ]); } public function updateProfile(UpdateProfileRequest $request): JsonResponse { $user = Auth::guard('admin')->user(); $user->update($request->validated()); return response()->json([ 'error' => false, 'message' => 'Cập nhật hồ sơ thành công.', 'data' => ['user' => new AdminUserResource($user->fresh())], ]); } public function updatePassword(UpdateAdminPasswordRequest $request): JsonResponse { $user = Auth::guard('admin')->user(); $user->update([ 'password' => Hash::make($request->string('password')->toString()), ]); $this->activityLogger->logAuth( 'password_changed', 'admin', $user->id, $user->username ?? $user->email, ); return response()->json([ 'error' => false, 'message' => 'Đổi mật khẩu thành công.', ]); } public function refresh(Request $request): JsonResponse { if (! $request->ajax()) { return response()->json([ 'error' => true, 'message' => 'Yêu cầu làm mới phiên không hợp lệ.', ], 403); } $request->validate([ 'refresh_token' => ['prohibited'], ]); $refreshToken = $request->cookie(self::REFRESH_COOKIE); if (! is_string($refreshToken) || $refreshToken === '') { return response()->json([ 'error' => true, 'message' => 'Phiên ghi nhớ đăng nhập không tồn tại.', ], 401)->withCookie($this->forgetRefreshCookie()); } try { $result = $this->refreshTokens->rotate($refreshToken); } catch (ValidationException) { return response()->json([ 'error' => true, 'message' => 'Refresh token không hợp lệ hoặc đã hết hạn.', ], 401)->withCookie($this->forgetRefreshCookie()); } return response()->json([ 'error' => false, 'data' => [ 'token' => $result['token'], 'token_type' => 'bearer', 'expires_in' => config('jwt.ttl') * 60, 'refresh_expires_in' => $result['refresh_expires_in'], 'user' => new AdminUserResource($result['user']), ], ])->withCookie($this->refreshCookie($result['refresh_token'], $result['refresh_expires_in'])); } public function logout(): JsonResponse { $refreshToken = request()->cookie(self::REFRESH_COOKIE); if (is_string($refreshToken) && $refreshToken !== '') { $this->refreshTokens->revoke($refreshToken); } $user = Auth::guard('admin')->user(); if ($user) { $this->activityLogger->logAuth('logout', 'admin', $user->id, $user->username ?? $user->email); } Auth::guard('admin')->logout(); return response()->json([ 'error' => false, 'message' => 'Đã đăng xuất thành công.', ])->withCookie($this->forgetRefreshCookie()); } private function refreshCookie(string $token, int $maxAgeSeconds): Cookie { return cookie( self::REFRESH_COOKIE, $token, (int) ceil($maxAgeSeconds / 60), self::REFRESH_COOKIE_PATH, null, true, true, false, app()->isLocal() ? 'none' : 'strict', ); } private function forgetRefreshCookie(): Cookie { return cookie()->forget(self::REFRESH_COOKIE, self::REFRESH_COOKIE_PATH); } }