diff --git a/assets/css/custom.css b/assets/css/custom.css index 819b46fa..7fee346e 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -440,6 +440,19 @@ .login-container { padding: 1rem; } +} + +@media (min-width: 992px) { + .login-container { + max-width: 600px; + padding: 0; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .login-container { + max-width: 500px; + } .login-form-wrapper { padding: 2rem 1.5rem; } diff --git a/auth_handler.php b/auth_handler.php index f5278c1d..c887995d 100644 --- a/auth_handler.php +++ b/auth_handler.php @@ -1,55 +1,232 @@ 'danger', 'message' => 'درخواست نامعتبر است.']; + header('Location: login.php'); + exit; } -// Retrieve user info from session -$google_user = $_SESSION['google_user_info']; -$email = $google_user['email']; -$fullName = $google_user['name']; -$nameParts = explode(' ', $fullName, 2); -$firstName = $nameParts[0]; -$lastName = isset($nameParts[1]) ? $nameParts[1] : ''; - -// Clear the temporary session data -unset($_SESSION['google_user_info']); - -try { +function handle_resend_otp() { + header('Content-Type: application/json'); $pdo = db(); - $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?"); - $stmt->execute([$email]); - $user = $stmt->fetch(); - if ($user) { - // User exists, log them in - $_SESSION['user_id'] = $user['id']; - $_SESSION['user_name'] = trim($user['first_name'] . ' ' . $user['last_name']); - $_SESSION['is_admin'] = $user['is_admin']; - } else { - // User does not exist, create a new one - $insertStmt = $pdo->prepare("INSERT INTO users (first_name, last_name, email, password, is_admin, created_at) VALUES (?, ?, ?, NULL, 0, NOW())"); - $insertStmt->execute([$firstName, $lastName, $email]); - $newUserId = $pdo->lastInsertId(); - - $_SESSION['user_id'] = $newUserId; - $_SESSION['user_name'] = $fullName; - $_SESSION['is_admin'] = 0; + if (!isset($_SESSION['otp_identifier'])) { + echo json_encode(['success' => false, 'message' => 'جلسه شما یافت نشد. لطفا دوباره تلاش کنید.']); + exit; } - header('Location: profile.php'); - exit(); + $identifier = $_SESSION['otp_identifier']; + $login_method = filter_var($identifier, FILTER_VALIDATE_EMAIL) ? 'email' : 'phone'; -} catch (Throwable $t) { - $error_message = 'Database error during Google auth processing: ' . $t->getMessage(); - error_log($error_message); - header('Location: login.php?error=db_error'); - exit(); + // Generate a new, cryptographically secure 6-digit OTP for resend + $otp = random_int(100000, 999999); + $expires = date('Y-m-d H:i:s', time() + (10 * 60)); // 10 minutes expiry + + try { + // A new OTP is inserted. The verification logic automatically picks the latest valid one. + $stmt = $pdo->prepare("INSERT INTO otp_codes (identifier, code, expires_at) VALUES (?, ?, ?)"); + $stmt->execute([$identifier, $otp, $expires]); + + // FOR TESTING: Always show the OTP for debugging purposes + $_SESSION['show_otp_for_debugging'] = $otp; + + echo json_encode(['success' => true, 'otp' => $otp, 'message' => 'کد جدید با موفقیت ارسال شد.']); + exit; + + } catch (Throwable $t) { + error_log("OTP Resend Error: " . $t->getMessage()); + echo json_encode(['success' => false, 'message' => 'خطایی در سیستم هنگام ارسال مجدد کد رخ داد.']); + exit; + } } + +function handle_send_otp() { + $pdo = db(); + $identifier = ''; + $login_method = ''; + + // Simplified and corrected logic + if (isset($_POST['email'])) { + // Trim whitespace from the email input + $identifier = trim($_POST['email']); + if (filter_var($identifier, FILTER_VALIDATE_EMAIL)) { + $login_method = 'email'; + } else { + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'لطفا یک ایمیل معتبر وارد کنید.']; + header('Location: login.php'); + exit; + } + } elseif (isset($_POST['phone'])) { + // Trim whitespace from the phone input + $identifier = trim($_POST['phone']); + if (preg_match('/^09[0-9]{9}$/', $identifier)) { + $login_method = 'phone'; + } else { + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'لطفا یک شماره تلفن معتبر (مانند 09123456789) وارد کنید.']; + header('Location: login.php'); + exit; + } + } else { + // Neither email nor phone was submitted + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'ایمیل یا شماره تلفن ارسال نشده است.']; + header('Location: login.php'); + exit; + } + // Generate a cryptographically secure 6-digit OTP + $otp = random_int(100000, 999999); + $expires = date('Y-m-d H:i:s', time() + (10 * 60)); // 10 minutes expiry + + try { + $stmt = $pdo->prepare("INSERT INTO otp_codes (identifier, code, expires_at) VALUES (?, ?, ?)"); + $stmt->execute([$identifier, $otp, $expires]); + + $_SESSION['otp_identifier'] = $identifier; + + // FOR TESTING: Always show the OTP for debugging purposes for both email and phone + $_SESSION['show_otp_for_debugging'] = $otp; + + + header('Location: verify.php'); + exit; + + } catch (Throwable $t) { + error_log("OTP Generation Error: " . $t->getMessage()); + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'خطایی در سیستم رخ داد. لطفا دوباره تلاش کنید.']; + header('Location: login.php'); + exit; + } +} + +function handle_verify_otp() { + if (empty($_POST['otp_code']) || empty($_SESSION['otp_identifier'])) { + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'جلسه شما منقضی شده است. لطفا دوباره تلاش کنید.']; + header('Location: login.php'); + exit; + } + + $pdo = db(); + $identifier = $_SESSION['otp_identifier']; + $otp_code = $_POST['otp_code']; + + try { + $stmt = $pdo->prepare("SELECT * FROM otp_codes WHERE identifier = ? AND code = ? AND expires_at > NOW() ORDER BY created_at DESC LIMIT 1"); + $stmt->execute([$identifier, $otp_code]); + $otp_entry = $stmt->fetch(); + + if ($otp_entry) { + // OTP is correct, clean up and log the user in + $delete_stmt = $pdo->prepare("DELETE FROM otp_codes WHERE identifier = ?"); + $delete_stmt->execute([$identifier]); + unset($_SESSION['otp_identifier']); + unset($_SESSION['show_otp_for_debugging']); + + // Determine if login was via email or phone + $is_email = filter_var($identifier, FILTER_VALIDATE_EMAIL); + $column = $is_email ? 'email' : 'phone'; + + $user_stmt = $pdo->prepare("SELECT * FROM users WHERE $column = ?"); + $user_stmt->execute([$identifier]); + $user = $user_stmt->fetch(); + + if ($user) { + // User exists, log them in + $_SESSION['user_id'] = $user['id']; + $_SESSION['user_name'] = trim($user['first_name'] . ' ' . $user['last_name']); + $_SESSION['is_admin'] = $user['is_admin']; + } else { + // User does not exist, create a new one + $insert_column = $is_email ? 'email' : 'phone'; + $insert_stmt = $pdo->prepare("INSERT INTO users ($insert_column, created_at) VALUES (?, NOW())"); + $insert_stmt->execute([$identifier]); + $newUserId = $pdo->lastInsertId(); + + $_SESSION['user_id'] = $newUserId; + $_SESSION['user_name'] = $identifier; // Placeholder name + $_SESSION['is_admin'] = 0; + } + + header('Location: profile.php'); + exit; + + } else { + // Invalid or expired OTP + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'کد تایید نامعتبر یا منقضی شده است.']; + header('Location: verify.php'); + exit; + } + + } catch (Throwable $t) { + // Reverted to production error handling + error_log("OTP Verification Error: " . $t->getMessage()); + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'خطایی در پایگاه داده رخ داد. لطفا دوباره تلاش کنید.']; + header('Location: verify.php'); + exit; + } +} + +function handle_google_callback() { + if (!isset($_SESSION['google_user_info'])) { + header('Location: login.php?error=google_auth_failed'); + exit(); + } + + $google_user = $_SESSION['google_user_info']; + $email = $google_user['email']; + $fullName = $google_user['name']; + $nameParts = explode(' ', $fullName, 2); + $firstName = $nameParts[0]; + $lastName = isset($nameParts[1]) ? $nameParts[1] : ''; + + // Clear the temporary session data + unset($_SESSION['google_user_info']); + + try { + $pdo = db(); + $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?"); + $stmt->execute([$email]); + $user = $stmt->fetch(); + + if ($user) { + $_SESSION['user_id'] = $user['id']; + $_SESSION['user_name'] = trim($user['first_name'] . ' ' . $user['last_name']); + $_SESSION['is_admin'] = $user['is_admin']; + } else { + $insertStmt = $pdo->prepare("INSERT INTO users (first_name, last_name, email, password, is_admin, created_at) VALUES (?, ?, ?, NULL, 0, NOW())"); + $insertStmt->execute([$firstName, $lastName, $email]); + $newUserId = $pdo->lastInsertId(); + + $_SESSION['user_id'] = $newUserId; + $_SESSION['user_name'] = $fullName; + $_SESSION['is_admin'] = 0; + } + + header('Location: profile.php'); + exit(); + + } catch (Throwable $t) { + error_log('Database error during Google auth processing: ' . $t->getMessage()); + header('Location: login.php?error=db_error'); + exit(); + } +} +?> \ No newline at end of file diff --git a/db/migrations/017_fix_otp_table.sql b/db/migrations/017_fix_otp_table.sql new file mode 100644 index 00000000..945c9ba1 --- /dev/null +++ b/db/migrations/017_fix_otp_table.sql @@ -0,0 +1,3 @@ +ALTER TABLE `otp_codes` +CHANGE COLUMN `email` `identifier` VARCHAR(255) NOT NULL, +CHANGE COLUMN `code_hash` `code` VARCHAR(255) NOT NULL; diff --git a/db/migrations/018_allow_null_names_in_users.sql b/db/migrations/018_allow_null_names_in_users.sql new file mode 100644 index 00000000..d2168d7f --- /dev/null +++ b/db/migrations/018_allow_null_names_in_users.sql @@ -0,0 +1,3 @@ +ALTER TABLE `users` +MODIFY COLUMN `first_name` VARCHAR(100) NULL, +MODIFY COLUMN `last_name` VARCHAR(100) NULL; \ No newline at end of file diff --git a/db/migrations/019_allow_null_email_in_users.sql b/db/migrations/019_allow_null_email_in_users.sql new file mode 100644 index 00000000..9370af3c --- /dev/null +++ b/db/migrations/019_allow_null_email_in_users.sql @@ -0,0 +1 @@ +ALTER TABLE `users` CHANGE `email` `email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL; \ No newline at end of file diff --git a/debug.log b/debug.log index b56438e1..b79d5a92 100644 --- a/debug.log +++ b/debug.log @@ -31,3 +31,4 @@ Google token response: Array ) Google Auth Exception: Token error: Bad Request +OTP Send Mail Error: PHPMailer error: SMTP Error: data not accepted. diff --git a/google_callback.php b/google_callback.php index a5018ba7..4b6adb97 100644 --- a/google_callback.php +++ b/google_callback.php @@ -6,8 +6,8 @@ session_start(); require_once 'vendor/autoload.php'; // Google API configuration -define('GOOGLE_CLIENT_ID', '915631311746-o6gk076l6lfvuboin99u2h8cgqilc0qk.apps.googleusercontent.com'); -define('GOOGLE_CLIENT_SECRET', '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'); +define('GOOGLE_CLIENT_ID', '915631311746-u10nasn59smdjn3ofle2a186vobmgll7.apps.googleusercontent.com'); +define('GOOGLE_CLIENT_SECRET', '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'); define('GOOGLE_REDIRECT_URL', 'https://atimah-leather.dev.flatlogic.app/google_callback.php'); // Check if the user has a temporary identifier from the initial login, and clear it. @@ -48,7 +48,7 @@ if (isset($_GET['code'])) { // Explicitly save the session data before redirecting. session_write_close(); - header('Location: auth_handler.php'); + header('Location: auth_handler.php?action=google_callback'); exit(); } catch (Throwable $t) { @@ -59,9 +59,7 @@ if (isset($_GET['code'])) { exit(); } } else { - $authUrl = $client->createAuthUrl(); - // Instead of redirecting, print the URL for debugging - echo "Please copy this URL and send it back to me:

"; - echo $authUrl; + // It's the initial request, redirect to Google's OAuth 2.0 server + header('Location: ' . $client->createAuthUrl()); exit(); } diff --git a/login.php b/login.php index 0bd9de35..d90ad26e 100644 --- a/login.php +++ b/login.php @@ -68,11 +68,25 @@ $body_class = "login-page-modern"; -
- - -
- لطفا یک ایمیل معتبر وارد کنید. + +
+
+ + +
+ لطفا یک ایمیل معتبر وارد کنید. +
+
+
+ + + @@ -99,7 +113,7 @@ $body_class = "login-page-modern"; diff --git a/verify.php b/verify.php index 0355ad32..91489296 100644 --- a/verify.php +++ b/verify.php @@ -59,11 +59,13 @@ $debug_otp = $_SESSION['show_otp_for_debugging'] ?? null; - -
- حالت آزمایشی: سرویس پیامک فعال نیست. کد شما: -
- +
+ +
+ حالت آزمایشی: سرویس پیامک فعال نیست. کد شما: +
+ +
@@ -80,13 +82,116 @@ $debug_otp = $_SESSION['show_otp_for_debugging'] ?? null;
- +