Files
Siro/backend/auth/driver/loginUsingCredentialsWithoutGoogle.php
T
Hamza-AyedandClaude Opus 5 35a66935aa Repair verification joins broken by the OTP key change; extend backfill
Storing the verification phone as a keyed HMAC fixed OTP lookups but broke
every query that joined those tables back to the account, because
phone_verification*.phone_number no longer holds the same value as
driver.phone / passengers.phone. Six joins were affected, and four of them
feed the `verified` flag that the rider and driver apps check at sign-in — so
this was already failing under the current CBC mode, not only after a switch
to GCM.

Accounts now carry phone_key, computed exactly as otpPhoneKey() does, and the
joins match on it. It is written at registration for both apps and populated
for existing rows by the backfill.

The backfill also covers the columns added for the remaining lookups:
users.email_bidx/phone_bidx and driver.national_bidx, which were migrated but
never populated, and honours a per-field prefix so phone_key reproduces
otpPhoneKey's exact output.

Insert column/value counts verified with a paren-aware parser after editing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 16:40:53 +03:00

113 lines
4.8 KiB
PHP

<?php
// ============================================================
// loginUsingCredentialsWithoutGoogle.php
// مخصص لدخول الفاحصين (Testers) بالإيميل والباسورد
// ============================================================
require_once __DIR__ . '/../../core/bootstrap.php';
$email = filterRequest('email');
$password = filterRequest('password');
$audience = filterRequest('aud') ?? 'siro-driver-android'; // الافتراضي
$fingerprint = filterRequest('fingerPrint') ?? filterRequest('fingerprint');
// 1. تطبيق حد معدل الطلبات (Rate Limiting) للفاحصين: 3 محاولات بالدقيقة لكل IP
$rateLimiter = new RateLimiter($redis);
$rateLimiter->enforce(RateLimiter::identifier(), 'tester_login');
if (!$email || !$password) {
echo json_encode(["status" => "failure", "message" => "Email and password are required"]);
exit();
}
// 2. التحقق من أن الحساب مخصص للفحص فقط (isTest check)
$allowedTesterEmailsEnv = getenv('ALLOWED_TESTER_EMAILS') ?: '';
$allowedEmails = array_filter(array_map('trim', explode(',', $allowedTesterEmailsEnv)));
if (empty($allowedEmails)) {
$allowedEmails = [
'driver_tester@siromove.com',
'passenger_tester@siromove.com',
];
}
$cleanEmail = strtolower(trim($email));
$isTester = in_array($cleanEmail, $allowedEmails) ||
substr($cleanEmail, -13) === '@siromove.com' ||
str_contains($cleanEmail, 'tester') ||
str_contains($cleanEmail, 'reviewer');
// تشفير الإيميل لاستخدامه في الاستعلام
$encryptedEmail = $encryptionHelper->encryptData($email);
try {
$con = Database::get('main');
// Auto-seed/create tester driver logic removed for security
// SQL لاسترجاع المستخدم بناءً على البريد الإلكتروني المشفر
$sql = "SELECT
driver.*,
phone_verification.is_verified,
CarRegistration.make,
CarRegistration.model,
CarRegistration.year
FROM driver
LEFT JOIN phone_verification ON phone_verification.phone_number = driver.phone_key
LEFT JOIN CarRegistration ON CarRegistration.driverID = driver.id
WHERE
driver.email = :email
LIMIT 1";
$stmt = $con->prepare($sql);
$stmt->bindParam(':email', $encryptedEmail);
$stmt->execute();
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if ($data) {
// التحقق من أن الحساب معلم كحساب فحص في قاعدة البيانات أو البيئة
$isTestInDb = (isset($data['is_test']) && $data['is_test'] == 1) || (isset($data['isTest']) && $data['isTest'] == 1);
if (!$isTestInDb && !$isTester) {
jsonError("Access denied. Not a tester account.");
exit();
}
// فحص الباسورد (في نظامنا، يمكن أن يكون الباسورد هو HMAC أو نص عادي للفاحصين)
// لنفترض أن الفاحص له باسورد عادي أو مشفر بـ bcrypt
if (password_verify($password, $data['password'])) {
unset($data['password']);
// فك تشفير الحقول الحساسة
$data['phone'] = $encryptionHelper->decryptData($data['phone']);
$data['email'] = $encryptionHelper->decryptData($data['email']);
$data['gender'] = $encryptionHelper->decryptData($data['gender']);
$data['birthdate'] = $encryptionHelper->decryptData($data['birthdate']);
$data['site'] = $encryptionHelper->decryptData($data['site']);
$data['first_name'] = $encryptionHelper->decryptData($data['first_name']);
$data['last_name'] = $encryptionHelper->decryptData($data['last_name']);
if(isset($data['employmentType'])) $data['employmentType'] = $encryptionHelper->decryptData($data['employmentType']);
if(isset($data['maritalStatus'])) $data['maritalStatus'] = $encryptionHelper->decryptData($data['maritalStatus']);
// توليد الـ JWT بصلاحية (tester) لتميزهم عن السائقين الفعليين
$jwtService = new JwtService($redis);
$jwt = $jwtService->generateAccessToken($data['id'], 'tester', $audience, $fingerprint);
echo json_encode([
"status" => "success",
"jwt" => $jwt,
"data" => [$data] // مطابق لنسق التطبيق الذي يتوقع مصفوفة
], JSON_UNESCAPED_UNICODE);
} else {
jsonError("Incorrect password.");
}
} else {
jsonError("User does not exist.");
}
} catch (Exception $e) {
error_log("[Tester Login Error] " . $e->getMessage());
jsonError("Server error occurred.");
} finally {
$stmt = null;
$con = null;
}
exit();
?>