fix menu tax in data and fix session when multiple user login
This commit is contained in:
@@ -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){
|
||||
|
||||
@@ -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();
|
||||
|
||||
167
app/Http/Middleware/ValidateApiTokenForWeb.php
Normal file
167
app/Http/Middleware/ValidateApiTokenForWeb.php
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user