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/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);