fix login and configurations
This commit is contained in:
@@ -22,6 +22,33 @@ if(true){
|
||||
$time_zone = "Asia/Bangkok";
|
||||
}
|
||||
|
||||
// ── Real-time Node.js server ─────────────────────────────────────────────────
|
||||
// Base URL the browser uses to reach nodejs/server.js (Socket.IO). Must match
|
||||
// PORT in nodejs/.env.
|
||||
if (!defined('NODE_PUBLIC_URL')) {
|
||||
define('NODE_PUBLIC_URL', 'http://localhost:3000');
|
||||
}
|
||||
// Server-to-server calls (PHP → Node /emit, and Node scheduler → PHP cron
|
||||
// endpoints) authenticate with this shared secret — must match EMIT_SECRET
|
||||
// in nodejs/.env exactly.
|
||||
if (!defined('NODE_EMIT_URL')) {
|
||||
define('NODE_EMIT_URL', 'http://127.0.0.1:3000/emit');
|
||||
}
|
||||
if (!defined('NODE_EMIT_SECRET')) {
|
||||
define('NODE_EMIT_SECRET', 'YOUR_NODE_EMIT_SECRET'); // must match nodejs/.env EMIT_SECRET
|
||||
}
|
||||
|
||||
// ── Usage packages ───────────────────────────────────────────────────────────
|
||||
// Keyed by company_list.package (defaults to 'starter'). Read by UsageGuard to
|
||||
// enforce daily/weekly action limits and which features lock once exceeded.
|
||||
$packages = [
|
||||
'starter' => [
|
||||
'daily_limit' => 30,
|
||||
'weekly_limit' => 100,
|
||||
'lock_on_limit' => ['dashboard'],
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
|
||||
// unique key — used for SMTP password encryption, keep consistent across deploys
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
* Fail either → return "Wrong OTP! Please try again."
|
||||
* 5. On success:
|
||||
* a. Concurrent-session check — if the account already has a session_token
|
||||
* set and session_last_seen is within the last hour, the login is blocked
|
||||
* with "already signed in on another device." A stale or NULL token allows
|
||||
* the login (user closed browser without logging out, or used back.php).
|
||||
* set and session_last_seen is within SESSION_ACTIVE_GRACE_SECONDS
|
||||
* (see below), the login is blocked with "already signed in on another
|
||||
* device." A stale or NULL token allows the login (previous session
|
||||
* expired naturally, or the user used back.php to log out explicitly).
|
||||
* b. session_regenerate_id(true) — prevents session fixation attack by
|
||||
* issuing a new session ID and deleting the old one.
|
||||
* c. Generate a fresh CSRF token and store in session.
|
||||
@@ -109,22 +110,46 @@ if (empty($_SESSION['skip_otp'])) {
|
||||
|
||||
// ── Step 4b: Concurrent session check ────────────────────────────────────────
|
||||
// Block the login if this account already has an active session.
|
||||
// "Active" = session_token is set AND session_last_seen is within the last hour.
|
||||
// A stale last_seen (user closed browser without logging out) expires after 1 h,
|
||||
// matching the PHP session GC maxlifetime configured in session.php.
|
||||
// "Active" = session_token is set AND session_last_seen is within the last
|
||||
// SESSION_ACTIVE_GRACE_SECONDS. db_auth.php refreshes session_last_seen on every
|
||||
// authenticated request (throttled to once per 60s), so a session that is truly
|
||||
// still in use on another device keeps re-touching this timestamp well within
|
||||
// the grace window below. A session that has actually ended — browser/tab closed,
|
||||
// cookie lost, PHP session GC'd — stops refreshing it and goes stale quickly.
|
||||
//
|
||||
// This window must stay well above the 60s throttle in db_auth.php (otherwise a
|
||||
// live second session could go stale between its own refreshes and let a login
|
||||
// through) but short enough that a real re-login isn't blocked for long after the
|
||||
// previous session actually ended. It intentionally does NOT match PHP's
|
||||
// session.gc_maxlifetime (3600s) — that timeout is about when PHP reclaims the
|
||||
// session file on disk, not about how quickly this check should stop treating a
|
||||
// dead session as "still active".
|
||||
// An explicit logout clears session_token to NULL, so back.php bypasses this.
|
||||
//
|
||||
// The staleness comparison is done entirely in SQL (session_last_seen vs MySQL's
|
||||
// own NOW()), not in PHP. session_last_seen is written with MySQL's NOW(), and
|
||||
// the MySQL server here runs on Asia/Bangkok time while PHP's default timezone is
|
||||
// UTC (config.php's $time_zone is never applied via date_default_timezone_set()).
|
||||
// Pulling the timestamp into PHP and comparing with strtotime()/time() silently
|
||||
// misreads that Bangkok wall-clock string as UTC — 7 hours in the future — which
|
||||
// made idle_seconds permanently negative and this check block every login,
|
||||
// regardless of window size. Comparing inside MySQL sidesteps the mismatch
|
||||
// without touching PHP's global timezone (which would ripple into every other
|
||||
// date()/time() call in the app).
|
||||
define('SESSION_ACTIVE_GRACE_SECONDS', 120);
|
||||
|
||||
$sth_active = $pdo1->prepare(
|
||||
"SELECT session_token, session_last_seen FROM user WHERE user_id = :uid LIMIT 1"
|
||||
"SELECT session_token,
|
||||
(session_last_seen IS NOT NULL
|
||||
AND session_last_seen > (NOW() - INTERVAL " . SESSION_ACTIVE_GRACE_SECONDS . " SECOND)) AS is_active
|
||||
FROM user WHERE user_id = :uid LIMIT 1"
|
||||
);
|
||||
$sth_active->execute([':uid' => $user_id]);
|
||||
$active_row = $sth_active->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!empty($active_row['session_token']) && !empty($active_row['session_last_seen'])) {
|
||||
$idle_seconds = time() - strtotime($active_row['session_last_seen']);
|
||||
if ($idle_seconds < 3600) {
|
||||
$answer['message'] = 'This account is currently signed in on another device. Please sign out from that session first.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
if (!empty($active_row['session_token']) && !empty($active_row['is_active'])) {
|
||||
$answer['message'] = 'This account is currently signed in on another device. Please sign out from that session first.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
// ── Step 4c: Claim session ────────────────────────────────────────────────────
|
||||
|
||||
@@ -342,7 +342,20 @@ if (password_verify(trim($data["password"]), $temp["password"])) {
|
||||
"company_id" => $default_company,
|
||||
"smtp" => $smtp_config,
|
||||
"subject" => "One Time Password (OTP) For reference number " . $reference_number,
|
||||
"message" => "Your OTP is " . $otp . " for reference number " . $reference_number,
|
||||
"message" => implode("\n", [
|
||||
"Dear WMS user,",
|
||||
"",
|
||||
"You requested a One-Time Password (OTP) to log in to WMS.",
|
||||
"",
|
||||
"Please use the OTP below to complete your request:",
|
||||
"• OTP code: " . $otp,
|
||||
"• Reference number: " . $reference_number,
|
||||
"",
|
||||
"Please note:",
|
||||
"• This code will expire in 3 minutes. Please complete your action promptly.",
|
||||
"• Do not share this code with anyone to keep your account secure.",
|
||||
"• If you did not request this code, please ignore this email.",
|
||||
]),
|
||||
"channel_name" => "WMS LOGIN OTP",
|
||||
"to" => $user_email,
|
||||
"key" => $pinkey,
|
||||
|
||||
@@ -115,7 +115,20 @@ if (password_verify(trim($data["password"]), $temp["password"])) {
|
||||
"company_id" => 0,
|
||||
"smtp" => $SMTP,
|
||||
"subject" => "One Time Password (OTP) For reference number " . $reference_number,
|
||||
"message" => "Your OTP is " . $otp . " for reference number " . $reference_number,
|
||||
"message" => implode("\n", [
|
||||
"Dear WMS user,",
|
||||
"",
|
||||
"You requested a One-Time Password (OTP) to log in to WMS.",
|
||||
"",
|
||||
"Please use the OTP below to complete your request:",
|
||||
"• OTP code: " . $otp,
|
||||
"• Reference number: " . $reference_number,
|
||||
"",
|
||||
"Please note:",
|
||||
"• This code will expire in 3 minutes. Please complete your action promptly.",
|
||||
"• Do not share this code with anyone to keep your account secure.",
|
||||
"• If you did not request this code, please ignore this email.",
|
||||
]),
|
||||
"channel_name" => "WMS LOGIN OTP ",
|
||||
"to" => $user_email,
|
||||
"key" => $pinkey,
|
||||
|
||||
Reference in New Issue
Block a user