From fe66f05057979d922aa5d46b7cb98c722d02053c Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 26 Aug 2026 23:46:48 +0300 Subject: [PATCH] Fix Redis session mismatch in AuthMiddleware and ensure instant Onboarding UI transition --- backend/app/Controllers/TeacherController.php | 7 ++- backend/app/Middlewares/AuthMiddleware.php | 48 +++++++++++-------- backend/app/Views/TeacherPortal.php | 12 ++++- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/backend/app/Controllers/TeacherController.php b/backend/app/Controllers/TeacherController.php index d8c77c7..a913b8b 100644 --- a/backend/app/Controllers/TeacherController.php +++ b/backend/app/Controllers/TeacherController.php @@ -26,7 +26,12 @@ class TeacherController $profile = Database::selectOne("SELECT * FROM teacher_profiles WHERE user_id = ? LIMIT 1", [$userId]); - $decryptedName = Security::decrypt($user['full_name']); + $rawName = (string)($user['full_name'] ?? ''); + $decryptedName = Security::decrypt($rawName) ?: $rawName; + if (empty($decryptedName)) { + $decryptedName = 'معلم جديد'; + } + $isComplete = !empty($profile) && !empty($profile['specialization']) && $decryptedName !== 'معلم جديد'; $response->json([ diff --git a/backend/app/Middlewares/AuthMiddleware.php b/backend/app/Middlewares/AuthMiddleware.php index df87803..5707082 100644 --- a/backend/app/Middlewares/AuthMiddleware.php +++ b/backend/app/Middlewares/AuthMiddleware.php @@ -5,6 +5,7 @@ namespace App\Middlewares; use App\Core\Request; use App\Core\Response; use App\Core\Security; +use App\Core\RedisClient; class AuthMiddleware { @@ -16,7 +17,7 @@ class AuthMiddleware $authHeader = $request->getHeader('authorization', ''); if (!$authHeader || !preg_match('/Bearer\s(\S+)/i', $authHeader, $matches)) { - $response->json(['error' => 'Unauthorized', 'message' => 'Token not provided or invalid format'], 401); + $response->status(401)->json(['error' => 'Unauthorized', 'message' => 'Token not provided or invalid format']); exit; } @@ -24,33 +25,42 @@ class AuthMiddleware $payload = Security::verifyJWT($token); if (!$payload) { - $response->json(['error' => 'Unauthorized', 'message' => 'Invalid or expired token'], 401); + $response->status(401)->json(['error' => 'Unauthorized', 'message' => 'Invalid or expired token']); exit; } - // Check if session is active in Redis - try { - $redis = \App\Core\RedisClient::getInstance(); - $userId = $payload['user_id']; - $sessionState = $redis->get("session:{$userId}:{$token}"); - - if (!$sessionState || $sessionState !== 'active') { - $response->json(['error' => 'Unauthorized', 'message' => 'Session has been revoked or expired'], 401); - exit; - } - } catch (\Exception $e) { - // If Redis is down, we fallback to just JWT verification - error_log("AuthMiddleware Redis Error: " . $e->getMessage()); - } - // Validate required custom payload elements if (!isset($payload['user_id']) || !isset($payload['role'])) { - $response->json(['error' => 'Unauthorized', 'message' => 'Malformed token payload structure'], 401); + $response->status(401)->json(['error' => 'Unauthorized', 'message' => 'Malformed token payload structure']); exit; } + $userId = (int)$payload['user_id']; + + // Check if session is active in Redis (Single Active Device / Session Tracking) + try { + $redis = RedisClient::getInstance(); + $sessionData = $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 = $payload['user_id']; + $request->user_id = $userId; $request->role = $payload['role']; + $request->uuid = $payload['uuid'] ?? null; } } diff --git a/backend/app/Views/TeacherPortal.php b/backend/app/Views/TeacherPortal.php index 2b4e997..4b6bb72 100644 --- a/backend/app/Views/TeacherPortal.php +++ b/backend/app/Views/TeacherPortal.php @@ -955,7 +955,17 @@ class TeacherPortal if (res.ok && data.status === 'success' && data.data?.token) { const token = data.data.token; localStorage.setItem('saqel_teacher_jwt', token); - await checkTeacherSession(token); + + const user = data.data.user; + // Immediate UI Transition + if (user && user.name && user.name !== 'معلم جديد' && user.name !== 'مستخدم صَقِل') { + renderDashboard(user); + } else { + switchToOnboardingStep(user?.name || ''); + } + + // Sync profile status in background + checkTeacherSession(token); } else { showError(data.message || 'رمز التحقق غير صحيح'); }