is('api/*')) { return $next($request); } $user = Auth::user(); $sessionToken = Session::get('api_token'); // Jika tidak ada token di session, generate token baru if (!$sessionToken) { $this->generateNewToken($user); return $next($request); } // Validasi token API if (!$this->isTokenValid($sessionToken, $user)) { // Token invalid, check apakah ada user lain yang login if ($this->hasOtherUserLoggedIn($user)) { // User lain sudah login, force logout user ini $this->forceLogout($request, 'User lain telah login. Silakan login ulang.'); return $this->redirectToLogin($request, 'User lain telah login. Silakan login ulang.'); } else { // Generate token baru jika tidak ada user lain $this->generateNewToken($user); } } return $next($request); } /** * Check apakah token API masih valid */ private function isTokenValid($sessionToken, $user): bool { if (!$sessionToken || !$user) { return false; } // Extract plain token dari session token $tokenParts = explode('|', $sessionToken); if (count($tokenParts) !== 2) { return false; } $plainToken = $tokenParts[1]; // Check token di database $validToken = PersonalAccessToken::where('tokenable_id', $user->id) ->where('tokenable_type', get_class($user)) ->where('token', hash('sha256', $plainToken)) ->where(function($query) { $query->whereNull('expires_at') ->orWhere('expires_at', '>', now()); }) ->first(); return $validToken !== null; } /** * Check apakah ada user lain yang login (token baru dibuat) */ private function hasOtherUserLoggedIn($currentUser): bool { $sessionUserId = Session::get('user_id'); // Jika ada user_id di session tapi tidak match dengan current user if ($sessionUserId && $sessionUserId != $currentUser->id) { return true; } // Check apakah ada token aktif lain untuk user ini $activeTokens = PersonalAccessToken::where('tokenable_id', $currentUser->id) ->where('tokenable_type', get_class($currentUser)) ->where(function($query) { $query->whereNull('expires_at') ->orWhere('expires_at', '>', now()); }) ->count(); // Jika tidak ada token aktif, kemungkinan user lain sudah login return $activeTokens === 0; } /** * Generate token baru untuk user */ private function generateNewToken($user): void { // Hapus token lama PersonalAccessToken::where('tokenable_id', $user->id) ->where('tokenable_type', get_class($user)) ->delete(); // Generate token baru $tokenName = config('app.name', 'Laravel') . '-' . $user->id . '-' . time(); $token = $user->createToken($tokenName, ['*'], now()->addDays(30))->plainTextToken; // Simpan token di session Session::put('api_token', $token); Session::put('user_id', $user->id); Session::put('login_timestamp', now()->timestamp); } /** * Force logout user dan clear semua sessions */ private function forceLogout(Request $request, string $reason = 'Session tidak valid'): void { $user = Auth::user(); if ($user) { // Delete all tokens for this user PersonalAccessToken::where('tokenable_id', $user->id) ->where('tokenable_type', get_class($user)) ->delete(); } // Clear session Session::forget(['api_token', 'user_id', 'login_timestamp']); Auth::guard('web')->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); } /** * Redirect ke login dengan pesan error */ private function redirectToLogin(Request $request, string $message): Response { if ($request->expectsJson() || $request->ajax()) { return response()->json([ 'error' => $message, 'redirect' => route('login'), 'force_logout' => true ], 401); } return redirect()->route('login')->with('error', $message); } }