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";
-