enforce(RateLimiter::identifier(), 'otp'); try { $con = Database::get('main'); // 1. جلب بيانات المسؤول عبر البصمة (مصدر موثوق وغير مشفر) $fpHash = hash('sha256', $fingerprint); $stmt = $con->prepare("SELECT * FROM adminUser WHERE fingerprint_hash = :fp LIMIT 1"); $stmt->execute([':fp' => $fpHash]); $admin = $stmt->fetch(PDO::FETCH_ASSOC); if (!$admin) { jsonError("المسؤول غير موجود أو البصمة غير مطابقة."); exit; } // 2. رقم الهاتف المشفر (للاستخدام في جدول OTP) $encryptedPhone = $admin['phone'] ?? ''; // فك تشفيره لو احتجنا إرساله أو عرضه، لكن هنا نحن نحتاج المشفر للبحث // $phone = $encryptionHelper->decryptData($encryptedPhone); // هاش الرمز (OTP) القادم من التطبيق للمقارنة $otpHash = hash('sha256', (string)$otp); // 3. التحقق من الـ OTP $stmt = $con->prepare("SELECT * FROM token_verification_admin WHERE phone_number = ? AND token = ? AND expiration_time >= NOW()"); $stmt->execute([$encryptedPhone, $otpHash]); if ($stmt->rowCount() === 0) { jsonError("رمز التحقق غير صالح أو منتهي الصلاحية."); exit; } // حذف الرمز بعد استخدامه لمرة واحدة (باستخدام الرقم المشفر) $con->prepare("DELETE FROM token_verification_admin WHERE phone_number = ?")->execute([$encryptedPhone]); // 4. تحديث وتأكيد بصمة المتصفح/الجهاز الحالية للمسؤول في قاعدة البيانات بعد التحقق الناجح من OTP $encFpRaw = ($encryptionHelper && !empty($fingerprint)) ? $encryptionHelper->encryptData($fingerprint) : $fingerprint; $updateFpStmt = $con->prepare("UPDATE adminUser SET fingerprint = :fp_raw, fingerprint_hash = :fp WHERE id = :id"); $updateFpStmt->execute([ ':fp_raw' => $encFpRaw, ':fp' => $fpHash, ':id' => $admin['id'] ]); $admin['fingerprint_hash'] = $fpHash; // 5. إصدار التوكن النهائي $jwtService = new JwtService($redis); $role = $admin['role'] ?? 'admin'; // إلغاء التوكن القديم إذا وجد في Redis (Token Revocation) if ($redis) { $oldJti = $redis->get("active_jti:" . $admin['id']); if ($oldJti) { $jwtService->revokeToken($oldJti, 3600); } } $jwt = $jwtService->generateAccessToken($admin['id'], $role, $audience, $fingerprint); // فك تشفير البيانات للعرض if ($encryptionHelper && !empty($admin['name'])) { $admin['name'] = $encryptionHelper->decryptData($admin['name']) ?: $admin['name']; } unset($admin['password']); printSuccess([ "message" => "Login successful", "admin" => $admin, "jwt" => $jwt, "expires_in" => 3600 ]); } catch (Exception $e) { error_log("[Admin Verify OTP Error] " . $e->getMessage()); jsonError("An internal error occurred. Please try again later."); }