fix menu tax in data and fix session when multiple user login

This commit is contained in:
arifal hidayat
2025-08-07 00:51:46 +07:00
parent 0abf278aa3
commit af05a39a82
13 changed files with 1209 additions and 36 deletions

View File

@@ -36,7 +36,9 @@ class UsersController extends Controller
return UserResource::collection($query->paginate(config('app.paginate_per_page', 50)));
}
public function logout(Request $request){
$request->user()->tokens()->delete();
\Laravel\Sanctum\PersonalAccessToken::where('tokenable_id', $request->user()->id)
->where('tokenable_type', get_class($request->user()))
->delete();
return response()->json(['message' => 'logged out successfully']);
}
public function store(UsersRequest $request){

View File

@@ -37,7 +37,9 @@ class AuthenticatedSessionController extends Controller
$user = Auth::user();
// Hapus token lama jika ada
$user->tokens()->delete();
\Laravel\Sanctum\PersonalAccessToken::where('tokenable_id', $user->id)
->where('tokenable_type', get_class($user))
->delete();
// Buat token untuk API dengan scope dan expiration
$tokenName = config('app.name', 'Laravel') . '-' . $user->id . '-' . time();
@@ -47,6 +49,10 @@ class AuthenticatedSessionController extends Controller
// Simpan token di session untuk digunakan di frontend
session(['api_token' => $token]);
// Simpan timestamp login untuk validasi multi-user
session(['login_timestamp' => now()->timestamp]);
session(['user_id' => $user->id]);
return redirect()->intended(RouteServiceProvider::HOME);
}
@@ -66,7 +72,9 @@ class AuthenticatedSessionController extends Controller
}
// Delete existing tokens
$user->tokens()->delete();
\Laravel\Sanctum\PersonalAccessToken::where('tokenable_id', $user->id)
->where('tokenable_type', get_class($user))
->delete();
// Generate new token
$tokenName = config('app.name', 'Laravel') . '-' . $user->id . '-' . time();
@@ -107,7 +115,9 @@ class AuthenticatedSessionController extends Controller
public function destroy(Request $request)
{
if($request->user()){
$request->user()->tokens()->delete();
\Laravel\Sanctum\PersonalAccessToken::where('tokenable_id', $request->user()->id)
->where('tokenable_type', get_class($request->user()))
->delete();
}
Auth::guard('web')->logout();

View File

@@ -0,0 +1,167 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Laravel\Sanctum\PersonalAccessToken;
use Symfony\Component\HttpFoundation\Response;
class ValidateApiTokenForWeb
{
/**
* Handle an incoming request.
* Middleware ini memvalidasi token API untuk web requests
* dan melakukan auto-logout jika token tidak valid
*/
public function handle(Request $request, Closure $next): Response
{
// Skip validation untuk non-authenticated routes
if (!Auth::check()) {
return $next($request);
}
// Skip validation untuk API routes (sudah ditangani oleh auth:sanctum)
if ($request->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);
}
}