From 0c9f43efcf5ab2188c1638df7328e0a4fc0adee3 Mon Sep 17 00:00:00 2001 From: Konstantinos Spartalis Date: Mon, 16 Mar 2026 19:40:39 +0200 Subject: [PATCH 1/8] test --- .../OPNsense/Base/ControllerBase.php | 1 + .../OPNsense/Base/ControllerRoot.php | 6 +-- .../mvc/app/views/layouts/default.volt | 2 + src/opnsense/www/js/opnsense.js | 52 +++++++++++++++++++ src/www/head.inc | 4 ++ 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerBase.php b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerBase.php index 89afe9c367e..e9a905cbbbb 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerBase.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerBase.php @@ -397,6 +397,7 @@ public function beforeExecuteRoute(Dispatcher $dispatcher) $this->view->session_username = !empty($_SESSION['Username']) ? $_SESSION['Username'] : '(unknown)'; $this->view->system_hostname = $cnf->object()->system->hostname; $this->view->system_domain = $cnf->object()->system->domain; + $this->view->session_timeout = $this->session_timeout; if (isset($this->view->menuBreadcrumbs[0]['name'])) { $output = []; diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php index 9d4c0e91c5a..9c873c795e6 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php @@ -130,9 +130,9 @@ public function doAuth() { $cnf = Config::getInstance()->object(); if (!empty($cnf->system->webgui->session_timeout)) { - $session_timeout = $cnf->system->webgui->session_timeout * 60; + $this->session_timeout = $cnf->system->webgui->session_timeout * 60; } else { - $session_timeout = 14400; + $this->session_timeout = 14400; } $redirect_uri = "/?url=" . $_SERVER['REQUEST_URI']; if ($this->session->has("Username") == false) { @@ -147,7 +147,7 @@ public function doAuth() return false; } elseif ( $this->session->has("last_access") - && $this->session->get("last_access") < (time() - $session_timeout) + && $this->session->get("last_access") < (time() - $this->session_timeout) ) { // session expired / cleanup session data $this->getLogger('audit')->notice(sprintf( diff --git a/src/opnsense/mvc/app/views/layouts/default.volt b/src/opnsense/mvc/app/views/layouts/default.volt index 1a20186c331..731f97c11cd 100644 --- a/src/opnsense/mvc/app/views/layouts/default.volt +++ b/src/opnsense/mvc/app/views/layouts/default.volt @@ -42,6 +42,7 @@ From 3037da38eeb9921ddaeda9932ba3d7dd1e0abf24 Mon Sep 17 00:00:00 2001 From: Konstantinos Spartalis Date: Mon, 16 Mar 2026 19:47:39 +0200 Subject: [PATCH 2/8] Update ControllerRoot.php --- .../mvc/app/controllers/OPNsense/Base/ControllerRoot.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php index 9c873c795e6..f3b7ceb2213 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php @@ -60,6 +60,11 @@ class ControllerRoot extends Controller */ protected $langcode = 'en_US'; + /** + * @var int session timeout in seconds + */ + public $session_timeout = 14400; + /** * set system language according to configuration */ From 5d4d248663ade6126cd121458f5c8896830e90b7 Mon Sep 17 00:00:00 2001 From: Konstantinos Spartalis Date: Mon, 16 Mar 2026 20:55:34 +0200 Subject: [PATCH 3/8] fix ajax + clock reset --- src/opnsense/www/js/opnsense.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/opnsense/www/js/opnsense.js b/src/opnsense/www/js/opnsense.js index 6dd5b9c5502..d553f57f9e0 100644 --- a/src/opnsense/www/js/opnsense.js +++ b/src/opnsense/www/js/opnsense.js @@ -285,9 +285,6 @@ function ajaxCall(url, sendData, callback) { dataType:'json', contentType: 'application/json', complete: function(data, status) { - if (status === "success" && typeof resetSessionTimeout === 'function') { - resetSessionTimeout(); - } if (callback != null) { if ('responseJSON' in data) { callback(data['responseJSON'], status); @@ -314,9 +311,6 @@ function ajaxGet(url,sendData,callback) { dataType:'json', contentType: 'application/json', complete: function(data,status) { - if (status === "success" && typeof resetSessionTimeout === 'function') { - resetSessionTimeout(); - } if (callback != null) { if ('responseJSON' in data) { callback(data['responseJSON'], status); @@ -396,7 +390,9 @@ function resetSessionTimeout() { * Initializes the auto-logout tracking mechanism. */ function initSessionTimeout() { - // Ensure window.sessionTimeout is set + if ($('input[name="usernamefld"]').length > 0 || window.location.href.includes('?url=')) { + return; + } if (typeof window.sessionTimeout !== 'number' || window.sessionTimeout <= 0) { return; } From aee49224057c705d733704415bae71f63b2b42f6 Mon Sep 17 00:00:00 2001 From: Konstantinos Spartalis Date: Mon, 16 Mar 2026 21:34:18 +0200 Subject: [PATCH 4/8] :) --- .../app/controllers/OPNsense/Base/ControllerRoot.php | 7 ++++++- src/opnsense/www/js/opnsense.js | 10 ++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php index f3b7ceb2213..cfdc63a59fb 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerRoot.php @@ -169,7 +169,12 @@ public function doAuth() $this->setLang(); - $this->session->set("last_access", time()); + $is_ajax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; + $is_post = $_SERVER['REQUEST_METHOD'] === 'POST'; + + if (!$is_ajax || $is_post) { + $this->session->set("last_access", time()); + } // Authorization using legacy acl structure $acl = new ACL(); diff --git a/src/opnsense/www/js/opnsense.js b/src/opnsense/www/js/opnsense.js index d553f57f9e0..0683eb56135 100644 --- a/src/opnsense/www/js/opnsense.js +++ b/src/opnsense/www/js/opnsense.js @@ -408,12 +408,18 @@ function initSessionTimeout() { }); setInterval(function() { - let lastActive = parseInt(localStorage.getItem(ACTIVITY_KEY) || Date.now(), 10); + let activeKeyStr = localStorage.getItem(ACTIVITY_KEY); + + if (!activeKeyStr) { + window.location.reload(); + return; + } + + let lastActive = parseInt(activeKeyStr, 10); let timeIdleMs = Date.now() - lastActive; if (timeIdleMs > sessionTimeoutMs) { localStorage.removeItem(ACTIVITY_KEY); - window.location.reload(); } }, 5000); From 8916fe83a65aeb40524fa67561fc48d4a5e62be3 Mon Sep 17 00:00:00 2001 From: Konstantinos Spartalis Date: Mon, 16 Mar 2026 21:55:20 +0200 Subject: [PATCH 5/8] fix CSRF --- src/opnsense/www/js/opnsense.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/opnsense/www/js/opnsense.js b/src/opnsense/www/js/opnsense.js index 0683eb56135..7a19c1570c3 100644 --- a/src/opnsense/www/js/opnsense.js +++ b/src/opnsense/www/js/opnsense.js @@ -391,8 +391,14 @@ function resetSessionTimeout() { */ function initSessionTimeout() { if ($('input[name="usernamefld"]').length > 0 || window.location.href.includes('?url=')) { + setInterval(function() { + if (localStorage.getItem(ACTIVITY_KEY)) { + window.location.reload(); + } + }, 3000); return; } + if (typeof window.sessionTimeout !== 'number' || window.sessionTimeout <= 0) { return; } From 87b40016ffe7d6b39f5a8a196f7929b967b9859d Mon Sep 17 00:00:00 2001 From: Konstantinos Spartalis Date: Tue, 17 Mar 2026 01:54:47 +0200 Subject: [PATCH 6/8] v4 try to login --- src/opnsense/mvc/app/views/layouts/default.volt | 1 + src/www/authgui.inc | 14 +++++++++++++- src/www/head.inc | 3 +++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/opnsense/mvc/app/views/layouts/default.volt b/src/opnsense/mvc/app/views/layouts/default.volt index 731f97c11cd..277d159062b 100644 --- a/src/opnsense/mvc/app/views/layouts/default.volt +++ b/src/opnsense/mvc/app/views/layouts/default.volt @@ -335,6 +335,7 @@ searchColumns: "{{ lang._('Search columns') }}", expand: "{{ lang._('Click to expand/collapse cell') }}" }); + localStorage.setItem('opnsense_auth_sync', Date.now().toString()); diff --git a/src/www/authgui.inc b/src/www/authgui.inc index 67cf31d9b11..c28ef808c28 100644 --- a/src/www/authgui.inc +++ b/src/www/authgui.inc @@ -380,7 +380,19 @@ function display_login_form($Login_Error) - + diff --git a/src/www/head.inc b/src/www/head.inc index 03624882be0..eed55353d8b 100644 --- a/src/www/head.inc +++ b/src/www/head.inc @@ -256,4 +256,7 @@ $pagetitle .= html_safe(sprintf(' | %s.%s', $config['system']['hostname'], $conf + From 75c858556251a966ca50f038236c9e870ad34bf2 Mon Sep 17 00:00:00 2001 From: Konstantinos Spartalis Date: Tue, 17 Mar 2026 02:19:43 +0200 Subject: [PATCH 7/8] add some time padding --- src/opnsense/www/js/opnsense.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opnsense/www/js/opnsense.js b/src/opnsense/www/js/opnsense.js index 7a19c1570c3..86112220776 100644 --- a/src/opnsense/www/js/opnsense.js +++ b/src/opnsense/www/js/opnsense.js @@ -424,7 +424,7 @@ function initSessionTimeout() { let lastActive = parseInt(activeKeyStr, 10); let timeIdleMs = Date.now() - lastActive; - if (timeIdleMs > sessionTimeoutMs) { + if (timeIdleMs > (sessionTimeoutMs + 10000)) { localStorage.removeItem(ACTIVITY_KEY); window.location.reload(); } From f2257df5d2db7e566f5ca8ae3bb84187930745da Mon Sep 17 00:00:00 2001 From: Konstantinos Spartalis Date: Fri, 10 Apr 2026 03:11:12 +0300 Subject: [PATCH 8/8] push from personal usage --- src/opnsense/www/js/opnsense.js | 36 +++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/opnsense/www/js/opnsense.js b/src/opnsense/www/js/opnsense.js index 86112220776..92933951222 100644 --- a/src/opnsense/www/js/opnsense.js +++ b/src/opnsense/www/js/opnsense.js @@ -374,14 +374,22 @@ function download_content(payload, filename, file_type) { const ACTIVITY_KEY = 'opn_last_activity'; let sessionThrottleTimer = null; +let lastPingTime = Date.now(); + /** * Resets the shared local storage timestamp. */ function resetSessionTimeout() { if (!sessionThrottleTimer) { sessionThrottleTimer = setTimeout(function() { - localStorage.setItem(ACTIVITY_KEY, Date.now().toString()); + let now = Date.now(); + localStorage.setItem(ACTIVITY_KEY, now.toString()); sessionThrottleTimer = null; + + if ((now - lastPingTime) > 300000) { + lastPingTime = now; + $.post('/api/core/menu/search'); + } }, 1000); } } @@ -390,15 +398,6 @@ function resetSessionTimeout() { * Initializes the auto-logout tracking mechanism. */ function initSessionTimeout() { - if ($('input[name="usernamefld"]').length > 0 || window.location.href.includes('?url=')) { - setInterval(function() { - if (localStorage.getItem(ACTIVITY_KEY)) { - window.location.reload(); - } - }, 3000); - return; - } - if (typeof window.sessionTimeout !== 'number' || window.sessionTimeout <= 0) { return; } @@ -409,10 +408,25 @@ function initSessionTimeout() { localStorage.setItem(ACTIVITY_KEY, Date.now().toString()); } - $(document).on('mousemove keydown click scroll touchstart', function() { + $(document).on('mousemove keydown click scroll touchstart', function(e) { + if (e.originalEvent === undefined || e.originalEvent.isTrusted === false) { + return; + } resetSessionTimeout(); }); + // Explicit logout synchronization + $(document).on('click', 'a[href*="logout"]', function() { + localStorage.setItem('opnsense_logout', Date.now().toString()); + localStorage.removeItem(ACTIVITY_KEY); + }); + + window.addEventListener('storage', function(e) { + if (e.key === 'opnsense_logout' || (e.key === ACTIVITY_KEY && !e.newValue)) { + window.location.reload(); + } + }); + setInterval(function() { let activeKeyStr = localStorage.getItem(ACTIVITY_KEY);