213 lines
9.0 KiB
PHP
213 lines
9.0 KiB
PHP
<?php
|
|
// File: backend/auth/otp/verify.php
|
|
// Unified OTP verification endpoint
|
|
|
|
require_once __DIR__ . '/../../core/bootstrap.php';
|
|
require_once __DIR__ . '/../../functions.php';
|
|
|
|
// 0. Rate Limiting: 3 محاولات OTP كل 5 دقائق لكل IP
|
|
$rateLimiter = new RateLimiter($redis);
|
|
$rateLimiter->enforce(RateLimiter::identifier(), 'otp_verify');
|
|
|
|
// 1. Fetch input parameters
|
|
$phone_number = filterRequest("phone_number");
|
|
if (empty($phone_number)) {
|
|
$phone_number = filterRequest("receiver");
|
|
}
|
|
|
|
$token_code = filterRequest("token_code");
|
|
if (empty($token_code)) {
|
|
$token_code = filterRequest("token");
|
|
}
|
|
|
|
$user_type = filterRequest("user_type");
|
|
$context = filterRequest("context"); // token_change | login (default)
|
|
|
|
// user_type is taken from request only (JWT not trusted without signature verification)
|
|
|
|
if (empty($phone_number)) {
|
|
jsonError("Phone number is required.");
|
|
exit;
|
|
}
|
|
|
|
if (empty($token_code)) {
|
|
jsonError("Verification token code is required.");
|
|
exit;
|
|
}
|
|
|
|
if (empty($user_type)) {
|
|
if (strpos($_SERVER['REQUEST_URI'], 'driver') !== false) {
|
|
$user_type = 'driver';
|
|
} else {
|
|
$user_type = 'passenger';
|
|
}
|
|
}
|
|
|
|
if (empty($user_type) || !in_array($user_type, ['passenger', 'driver', 'admin', 'service'])) {
|
|
jsonError("User type must be 'passenger', 'driver', 'admin', or 'service'.");
|
|
exit;
|
|
}
|
|
|
|
// 2. Establish DB Connection
|
|
try {
|
|
$con = Database::get('main');
|
|
} catch (Exception $e) {
|
|
http_response_code(500);
|
|
exit(json_encode(['error' => 'Database connection failed']));
|
|
}
|
|
|
|
// 3. Encrypt data to query
|
|
$encryptedPhone = $encryptionHelper->encryptData($phone_number);
|
|
$encryptedToken = $encryptionHelper->encryptData($token_code);
|
|
|
|
// 4. Verify based on user type
|
|
try {
|
|
if ($user_type === 'admin') {
|
|
$sql = "SELECT * FROM token_verification_admin
|
|
WHERE phone_number = :phone AND token = :token
|
|
AND expiration_time >= NOW() AND verified = 0";
|
|
$stmt = $con->prepare($sql);
|
|
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
|
|
$stmt->execute();
|
|
|
|
if ($stmt->rowCount() > 0) {
|
|
$deviceNumber = filterRequest("device_number") ?? '';
|
|
// adminUser stores unencrypted phone
|
|
$checkAdmin = $con->prepare("SELECT * FROM adminUser WHERE name = ?");
|
|
$checkAdmin->execute([$phone_number]);
|
|
$now = date("Y-m-d H:i:s");
|
|
|
|
if ($checkAdmin->rowCount() > 0) {
|
|
$update = $con->prepare("UPDATE adminUser SET device_number = ?, updated_at = ? WHERE name = ?");
|
|
$update->execute([$deviceNumber, $now, $phone_number]);
|
|
jsonSuccess(["message" => "verified and updated existing admin"]);
|
|
} else {
|
|
$insert = $con->prepare("INSERT INTO adminUser (device_number, name, created_at, updated_at) VALUES (?, ?, ?, ?)");
|
|
$insert->execute([$deviceNumber, $phone_number, $now, $now]);
|
|
jsonSuccess(["message" => "verified and new admin created"]);
|
|
}
|
|
} else {
|
|
jsonError("Your phone number could not be verified or the code is expired. Please try again.");
|
|
}
|
|
} elseif ($user_type === 'service') {
|
|
$sql = "SELECT `id` FROM `phone_verification_service`
|
|
WHERE `phone_number` = :phone AND `token_code` = :token
|
|
AND `expiration_time` > NOW() AND `is_verified` = 0";
|
|
$stmt = $con->prepare($sql);
|
|
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
|
|
$stmt->execute();
|
|
$result = $stmt->fetch();
|
|
|
|
if ($result) {
|
|
$sqlUpdate = "UPDATE `phone_verification_service` SET `is_verified` = 1 WHERE `phone_number` = :phone";
|
|
$stmtUpd = $con->prepare($sqlUpdate);
|
|
$stmtUpd->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmtUpd->execute();
|
|
jsonSuccess(null, "Your phone number has been verified.");
|
|
} else {
|
|
jsonError("Your phone number could not be verified or the code is expired. Please try again.");
|
|
}
|
|
} elseif ($user_type === 'driver') {
|
|
if ($context === 'token_change') {
|
|
$sql = "SELECT `id` FROM `token_verification_driver`
|
|
WHERE `phone_number` = :phone
|
|
AND `token` = :token
|
|
AND `expiration_time` > NOW() AND `verified` = 0";
|
|
|
|
$stmt = $con->prepare($sql);
|
|
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
|
|
$stmt->execute();
|
|
$result = $stmt->fetch();
|
|
|
|
if ($result) {
|
|
// Update driver verified status
|
|
$sqlUpdate = "UPDATE `token_verification_driver` SET `verified` = 1 WHERE `phone_number` = :phone";
|
|
$stmtUpd = $con->prepare($sqlUpdate);
|
|
$stmtUpd->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmtUpd->execute();
|
|
|
|
jsonSuccess(null, "Your phone number has been verified.");
|
|
} else {
|
|
jsonError("Your phone number could not be verified or the code is expired. Please try again.");
|
|
}
|
|
} else {
|
|
$sql = "SELECT `id` FROM `phone_verification`
|
|
WHERE `phone_number` = :phone
|
|
AND `token_code` = :token
|
|
AND `expiration_time` > NOW() AND `is_verified` = 0";
|
|
|
|
$stmt = $con->prepare($sql);
|
|
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
|
|
$stmt->execute();
|
|
$result = $stmt->fetch();
|
|
|
|
if ($result) {
|
|
// Update driver is_verified status
|
|
$sqlUpdate = "UPDATE `phone_verification` SET `is_verified` = 1 WHERE `phone_number` = :phone";
|
|
$stmtUpd = $con->prepare($sqlUpdate);
|
|
$stmtUpd->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmtUpd->execute();
|
|
|
|
jsonSuccess(null, "Your phone number has been verified.");
|
|
} else {
|
|
jsonError("Your phone number could not be verified or the code is expired. Please try again.");
|
|
}
|
|
}
|
|
} else {
|
|
if ($context === 'token_change') {
|
|
$sql = "SELECT `id` FROM `token_verification`
|
|
WHERE `phone_number` = :phone
|
|
AND `token` = :token
|
|
AND `expiration_time` > NOW() AND `verified` = 0";
|
|
|
|
$stmt = $con->prepare($sql);
|
|
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
|
|
$stmt->execute();
|
|
$result = $stmt->fetch();
|
|
|
|
if ($result) {
|
|
// Update passenger verified status
|
|
$sqlUpdate = "UPDATE `token_verification` SET `verified` = 1 WHERE `phone_number` = :phone";
|
|
$stmtUpd = $con->prepare($sqlUpdate);
|
|
$stmtUpd->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmtUpd->execute();
|
|
|
|
jsonSuccess(null, "Your phone number has been verified.");
|
|
} else {
|
|
jsonError("Your phone number could not be verified or the code is expired. Please try again.");
|
|
}
|
|
} else {
|
|
$sql = "SELECT `id` FROM `phone_verification_passenger`
|
|
WHERE `phone_number` = :phone
|
|
AND `token` = :token
|
|
AND `expiration_time` > NOW() AND `verified` = 0";
|
|
|
|
$stmt = $con->prepare($sql);
|
|
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
|
|
$stmt->execute();
|
|
$result = $stmt->fetch();
|
|
|
|
if ($result) {
|
|
// Update passenger verified status
|
|
$sqlUpdate = "UPDATE `phone_verification_passenger` SET `verified` = 1 WHERE `phone_number` = :phone";
|
|
$stmtUpd = $con->prepare($sqlUpdate);
|
|
$stmtUpd->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
|
|
$stmtUpd->execute();
|
|
|
|
jsonSuccess(null, "Your phone number has been verified.");
|
|
} else {
|
|
jsonError("Your phone number could not be verified or the code is expired. Please try again.");
|
|
}
|
|
}
|
|
}
|
|
} catch (PDOException $e) {
|
|
error_log("⚠️ [OTP DB Verify] Error: " . $e->getMessage());
|
|
jsonError("An error occurred during verification. Please try again.");
|
|
}
|