enforce(RateLimiter::identifier(), 'login'); try { $id = filterRequest('id') ?? ''; $password = filterRequest('password') ?? ''; $audience = filterRequest('aud') ?? ''; $allowed1 = getenv('allowedDriver1'); $allowed2 = getenv('allowedDriver2'); $allowedAudiences = array_values(array_filter([$allowed1, $allowed2])); if (empty($id) || empty($password) || empty($audience)) { jsonError('ID and password are required.', 400); } if (!in_array($audience, $allowedAudiences, true)) { jsonError('Invalid audience.', 400); } $con = Database::get('main'); // ── جلب بيانات المشرف ──────────────────────────────────── // ملاحظة: جدول admin_users سيتم إنشاؤه في Phase 4 (db_improvements.sql) $stmt = $con->prepare("SELECT id, password, email, role FROM admin_users WHERE username = :id OR email = :id LIMIT 1"); $stmt->execute([':id' => $id]); $admin = $stmt->fetch(); $startTime = microtime(true); if ($admin && password_verify($password, $admin['password'])) { $limiter->reset(RateLimiter::identifier(), 'login'); $jwtService = new JwtService($redis); // استخدام Role المخصص أو 'admin' $role = $admin['role'] ?? 'admin'; $jwt = $jwtService->generateAccessToken($admin['id'], $role, $audience); $refresh = $jwtService->generateRefreshToken($admin['id']); jsonSuccess([ 'jwt' => $jwt, 'refresh_token' => $refresh['token'], 'expires_in' => 900 ]); } else { // حماية من Timing Attack $elapsed = microtime(true) - $startTime; if ($elapsed < 0.1) usleep((int)((0.1 - $elapsed) * 1000000)); jsonError('Invalid ID or password.', 401); } } catch (PDOException $e) { securityLog("Admin Login PDO Error", ['msg' => $e->getMessage()]); jsonError('Login failed: Database error', 500); } catch (Exception $e) { securityLog("Admin Login Error", ['msg' => $e->getMessage()]); jsonError('Login failed: Server error', 500); }