From 35a66935aa51e00bbd56eb1d833006943c63269f Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sat, 25 Jul 2026 16:40:53 +0300 Subject: [PATCH] Repair verification joins broken by the OTP key change; extend backfill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- backend/auth/driver/loginFromGoogle.php | 2 +- .../driver/loginUsingCredentialsWithoutGoogle.php | 2 +- backend/auth/driver/register.php | 7 +++++-- backend/auth/passenger/loginFromGoogle.php | 2 +- .../loginUsingCredentialsWithoutGoogle.php | 2 +- backend/auth/passenger/register.php | 9 ++++++--- backend/scripts/backfill_blind_index.php | 15 ++++++++++++--- backend/scripts/migrate.php | 7 +++++++ backend/serviceapp/getJsonFile.php | 2 +- 9 files changed, 35 insertions(+), 13 deletions(-) diff --git a/backend/auth/driver/loginFromGoogle.php b/backend/auth/driver/loginFromGoogle.php index cf6d3df5..7c362afe 100644 --- a/backend/auth/driver/loginFromGoogle.php +++ b/backend/auth/driver/loginFromGoogle.php @@ -23,7 +23,7 @@ try { CarRegistration.make, CarRegistration.model, CarRegistration.year, df.is_claimed, inv.isInstall, inv.isGiftToken FROM driver - LEFT JOIN phone_verification ON phone_verification.phone_number = driver.phone + LEFT JOIN phone_verification ON phone_verification.phone_number = driver.phone_key LEFT JOIN driver_gifts df ON df.driver_id = driver.id LEFT JOIN CarRegistration ON CarRegistration.driverID = driver.id LEFT JOIN invites inv ON inv.driverId = driver.id diff --git a/backend/auth/driver/loginUsingCredentialsWithoutGoogle.php b/backend/auth/driver/loginUsingCredentialsWithoutGoogle.php index 871030b5..a73b4418 100644 --- a/backend/auth/driver/loginUsingCredentialsWithoutGoogle.php +++ b/backend/auth/driver/loginUsingCredentialsWithoutGoogle.php @@ -52,7 +52,7 @@ try { CarRegistration.model, CarRegistration.year FROM driver - LEFT JOIN phone_verification ON phone_verification.phone_number = driver.phone + LEFT JOIN phone_verification ON phone_verification.phone_number = driver.phone_key LEFT JOIN CarRegistration ON CarRegistration.driverID = driver.id WHERE driver.email = :email diff --git a/backend/auth/driver/register.php b/backend/auth/driver/register.php index 0af20a46..692579df 100644 --- a/backend/auth/driver/register.php +++ b/backend/auth/driver/register.php @@ -392,6 +392,8 @@ Therefore, do NOT assume a specific field is on the front or the back of a card. 'driver.name', trim(($data['first_name'] ?? '') . ' ' . ($data['last_name'] ?? '')) ) : null; + // مفتاح ربط جداول التحقق — يجب أن يطابق otpPhoneKey() حرفياً + $phoneKey = otpPhoneKey($data['phone'] ?? null); $toEncryptDriver = [ "phone","email","first_name","last_name","name_arabic","gender", @@ -439,7 +441,7 @@ Therefore, do NOT assume a specific field is on the front or the back of a card. first_name, last_name, accountBank, bankCode, employmentType, ai_data, user_input, maritalStatus, fullNameMaritial, expirationDate, created_at, updated_at, - phone_bidx, email_bidx, name_bidx + phone_bidx, email_bidx, name_bidx, phone_key ) VALUES ( :id, :phone, :email, :pwd, :gender, :license_type, :national_number, :name_arabic, :issue_date, :expiry_date, :license_categories, @@ -447,7 +449,7 @@ Therefore, do NOT assume a specific field is on the front or the back of a card. :first_name, :last_name, :accountBank, :bankCode, :employmentType, :ai_data, :user_input, :maritalStatus, :fullNameMaritial, :expirationDate, NOW(), NOW(), - :phone_bidx, :email_bidx, :name_bidx + :phone_bidx, :email_bidx, :name_bidx, :phone_key ) "; $insD = $con->prepare($sqlDriver); @@ -481,6 +483,7 @@ Therefore, do NOT assume a specific field is on the front or the back of a card. ':phone_bidx' => $phoneBidx, ':email_bidx' => $emailBidx, ':name_bidx' => $nameBidx, + ':phone_key' => $phoneKey, ]); if (!$okD) { $con->rollBack(); diff --git a/backend/auth/passenger/loginFromGoogle.php b/backend/auth/passenger/loginFromGoogle.php index 56edd98e..76ee1730 100644 --- a/backend/auth/passenger/loginFromGoogle.php +++ b/backend/auth/passenger/loginFromGoogle.php @@ -45,7 +45,7 @@ $sql = "SELECT t.fingerPrint AS fcm_fingerprint FROM passengers p LEFT JOIN phone_verification_passenger - ON phone_verification_passenger.phone_number = p.phone + ON phone_verification_passenger.phone_number = p.phone_key LEFT JOIN invitesToPassengers ON invitesToPassengers.inviterPassengerPhone = p.phone LEFT JOIN promos diff --git a/backend/auth/passenger/loginUsingCredentialsWithoutGoogle.php b/backend/auth/passenger/loginUsingCredentialsWithoutGoogle.php index 4c07f934..345e13a1 100644 --- a/backend/auth/passenger/loginUsingCredentialsWithoutGoogle.php +++ b/backend/auth/passenger/loginUsingCredentialsWithoutGoogle.php @@ -53,7 +53,7 @@ try { invitesToPassengers.isGiftToken FROM passengers p LEFT JOIN phone_verification_passenger - ON phone_verification_passenger.phone_number = p.phone + ON phone_verification_passenger.phone_number = p.phone_key LEFT JOIN invitesToPassengers ON invitesToPassengers.inviterPassengerPhone = p.phone WHERE p.email = :email OR (:email_bidx IS NOT NULL AND p.email_bidx = :email_bidx) diff --git a/backend/auth/passenger/register.php b/backend/auth/passenger/register.php index ed1290a7..fb0f7ded 100644 --- a/backend/auth/passenger/register.php +++ b/backend/auth/passenger/register.php @@ -109,6 +109,8 @@ try { $phoneBidx = $blindIndex ? $blindIndex->index('passengers.phone', $phoneNumber) : null; $emailBidx = $blindIndex ? $blindIndex->index('passengers.email', $email) : null; $nameBidx = $blindIndex ? $blindIndex->index('passengers.name', trim("$firstName $lastName")) : null; + // مفتاح ربط جداول التحقق — يجب أن يطابق otpPhoneKey() حرفياً + $phoneKey = otpPhoneKey($phoneNumber); $checkStmt = $con->prepare( "SELECT id FROM passengers WHERE phone = ? OR (? IS NOT NULL AND phone_bidx = ?)" @@ -128,8 +130,8 @@ try { error_log("$logTag Step 7: Inserting into passengers table..."); $insertStmt = $con->prepare(" - INSERT INTO passengers (id, first_name, last_name, email, phone, password, gender, birthdate, site, sosPhone, education, employmentType, maritalStatus, status, created_at, updated_at, phone_bidx, email_bidx, name_bidx) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NOW(), NOW(), ?, ?, ?) + INSERT INTO passengers (id, first_name, last_name, email, phone, password, gender, birthdate, site, sosPhone, education, employmentType, maritalStatus, status, created_at, updated_at, phone_bidx, email_bidx, name_bidx, phone_key) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NOW(), NOW(), ?, ?, ?, ?) "); $success = $insertStmt->execute([ $uniqueId, @@ -148,7 +150,8 @@ try { // فهارس البحث: تُكتب مع السجل حتى يكون قابلاً للبحث فوراً $phoneBidx, $emailBidx, - $nameBidx + $nameBidx, + $phoneKey ]); if (!$success) { diff --git a/backend/scripts/backfill_blind_index.php b/backend/scripts/backfill_blind_index.php index 8e7d8cd8..cee161ec 100644 --- a/backend/scripts/backfill_blind_index.php +++ b/backend/scripts/backfill_blind_index.php @@ -131,19 +131,27 @@ if (isset($options['audit'])) { $targets = [ 'driver' => [ - 'phone_bidx' => ['scope' => 'driver.phone', 'columns' => ['phone']], - 'email_bidx' => ['scope' => 'driver.email', 'columns' => ['email']], - 'name_bidx' => ['scope' => 'driver.name', 'columns' => ['first_name', 'last_name']], + 'phone_bidx' => ['scope' => 'driver.phone', 'columns' => ['phone']], + 'email_bidx' => ['scope' => 'driver.email', 'columns' => ['email']], + 'name_bidx' => ['scope' => 'driver.name', 'columns' => ['first_name', 'last_name']], + 'national_bidx' => ['scope' => 'driver.national', 'columns' => ['national_number']], + // نفس نطاق otpPhoneKey: يسمح بربط جداول التحقق بصاحب الحساب + 'phone_key' => ['scope' => 'otp.phone', 'columns' => ['phone'], 'prefix' => 'K:'], ], 'passengers' => [ 'phone_bidx' => ['scope' => 'passengers.phone', 'columns' => ['phone']], 'email_bidx' => ['scope' => 'passengers.email', 'columns' => ['email']], 'name_bidx' => ['scope' => 'passengers.name', 'columns' => ['first_name', 'last_name']], + 'phone_key' => ['scope' => 'otp.phone', 'columns' => ['phone'], 'prefix' => 'K:'], ], 'adminUser' => [ 'phone_bidx' => ['scope' => 'adminUser.phone', 'columns' => ['phone']], 'email_bidx' => ['scope' => 'adminUser.email', 'columns' => ['email']], ], + 'users' => [ + 'phone_bidx' => ['scope' => 'users.phone', 'columns' => ['phone']], + 'email_bidx' => ['scope' => 'users.email', 'columns' => ['email']], + ], ]; echo $dryRun ? "── DRY RUN — nothing will be written ──\n" : "── Backfilling blind indexes ──\n"; @@ -202,6 +210,7 @@ foreach ($targets as $table => $fields) { $value = implode(' ', $parts); $index = $blind->index($spec['scope'], $value); if ($index === null) continue; + if (!empty($spec['prefix'])) $index = $spec['prefix'] . $index; $set[] = "`$column` = :$column"; $params[":$column"] = $index; diff --git a/backend/scripts/migrate.php b/backend/scripts/migrate.php index 8f07915e..1538aeac 100644 --- a/backend/scripts/migrate.php +++ b/backend/scripts/migrate.php @@ -84,6 +84,11 @@ $columns = [ ['token_verification', 'phone_enc', "TEXT NULL DEFAULT NULL COMMENT 'الهاتف مشفّراً للاسترجاع'"], ['token_verification_driver', 'phone_enc', "TEXT NULL DEFAULT NULL COMMENT 'الهاتف مشفّراً للاسترجاع'"], + // مفتاح ربط جداول التحقق: نفس صيغة otpPhoneKey، حتى يمكن ربط + // phone_verification*.phone_number بصاحب الحساب دون مقارنة نصوص مشفّرة. + ['driver', 'phone_key', "VARCHAR(80) NULL DEFAULT NULL COMMENT 'مفتاح ربط جداول التحقق'"], + ['passengers', 'phone_key', "VARCHAR(80) NULL DEFAULT NULL COMMENT 'مفتاح ربط جداول التحقق'"], + // بقية الجداول التي يُبحث فيها بحقل مشفّر ['users', 'email_bidx', "CHAR(64) NULL DEFAULT NULL COMMENT 'HMAC لبريد موظف الخدمة'"], ['users', 'phone_bidx', "CHAR(64) NULL DEFAULT NULL COMMENT 'HMAC لهاتف موظف الخدمة'"], @@ -107,6 +112,8 @@ $indexes = [ ['users', 'idx_users_email_bidx', 'email_bidx'], ['users', 'idx_users_phone_bidx', 'phone_bidx'], ['driver', 'idx_driver_national_bidx', 'national_bidx'], + ['driver', 'idx_driver_phone_key', 'phone_key'], + ['passengers', 'idx_passengers_phone_key', 'phone_key'], ]; $applied = 0; diff --git a/backend/serviceapp/getJsonFile.php b/backend/serviceapp/getJsonFile.php index 74964888..d15e2ea3 100644 --- a/backend/serviceapp/getJsonFile.php +++ b/backend/serviceapp/getJsonFile.php @@ -22,7 +22,7 @@ $sql = " FROM phone_verification pv LEFT JOIN - driver d ON pv.phone_number = d.phone + driver d ON pv.phone_number = d.phone_key LEFT JOIN notesForDriverService n ON pv.phone_number = n.phone WHERE