getHeader('authorization', ''); if (!$authHeader || !preg_match('/Bearer\s(\S+)/i', $authHeader, $matches)) { $response->status(401)->json(['error' => 'Unauthorized', 'message' => 'Token not provided or invalid format']); exit; } $token = $matches[1]; $payload = Security::verifyJWT($token); if (!$payload) { $response->status(401)->json(['error' => 'Unauthorized', 'message' => 'Invalid or expired token']); exit; } // Validate required custom payload elements if (!isset($payload['user_id']) || !isset($payload['role'])) { $response->status(401)->json(['error' => 'Unauthorized', 'message' => 'Malformed token payload structure']); exit; } $userId = (int)$payload['user_id']; $role = (string)($payload['role'] ?? 'student'); // Check if session is active in Redis (Role-isolated single active session tracking) try { $redis = RedisClient::getInstance(); $sessionData = $redis->get("active_session:{$role}:{$userId}") ?: $redis->get("active_session:{$userId}"); if ($sessionData) { $session = json_decode($sessionData, true); $expectedSig = substr($token, -32); if (is_array($session) && isset($session['token_signature']) && $session['token_signature'] !== $expectedSig) { $response->status(401)->json([ 'error' => 'Unauthorized', 'message' => 'تم تسجيل الدخول من جهاز أو متصفح آخر. يرجى إعادة تسجيل الدخول.' ]); exit; } } } catch (\Exception $e) { // If Redis is temporarily unreachable, fallback gracefully to JWT verification error_log("AuthMiddleware Redis Warning: " . $e->getMessage()); } // Attach user info to the Request instance dynamically so controllers can use it $request->user_id = $userId; $request->role = $payload['role']; $request->uuid = $payload['uuid'] ?? null; } }