From 64a8504794ddd4017f16c59f02e6f42946c5ffed Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 20 Jun 2026 20:37:37 -0500 Subject: [PATCH] feat(support-pin): daily-rotating PIN via HMAC(token, date) PIN is derived from HMAC-SHA256(health_api_token, YYYY-MM-DD) so both client and HQ compute the same value independently. Rotates at UTC midnight. Shown on cpanel module header and component dashboard. --- .../admin/src/View/Dashboard/HtmlView.php | 23 +++++++++++++++++++ .../admin/tmpl/dashboard/default.php | 6 +++++ .../src/Dispatcher/Dispatcher.php | 6 +++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/source/packages/com_mokosuiteclient/admin/src/View/Dashboard/HtmlView.php b/source/packages/com_mokosuiteclient/admin/src/View/Dashboard/HtmlView.php index a0899a8f..1e85f947 100644 --- a/source/packages/com_mokosuiteclient/admin/src/View/Dashboard/HtmlView.php +++ b/source/packages/com_mokosuiteclient/admin/src/View/Dashboard/HtmlView.php @@ -26,6 +26,7 @@ class HtmlView extends BaseHtmlView protected $wafChartData = []; protected $loginChartData = []; protected $mokoExtensions = []; + public $supportPin = ''; public function display($tpl = null) { @@ -33,6 +34,28 @@ class HtmlView extends BaseHtmlView $this->plugins = $model->getFeaturePlugins(); $this->siteInfo = $model->getSiteInfo(); + + // Daily support PIN from health token + try + { + $db = \Joomla\CMS\Factory::getDbo(); + $db->setQuery( + $db->getQuery(true) + ->select($db->quoteName('params')) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote('mokosuiteclient')) + ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) + ->where($db->quoteName('folder') . ' = ' . $db->quote('system')) + ); + $token = (json_decode((string) $db->loadResult()))->health_api_token ?? ''; + + if (!empty($token)) + { + $hash = hash_hmac('sha256', gmdate('Y-m-d'), $token); + $this->supportPin = 'MOKO-' . strtoupper(substr($hash, 0, 4)) . '-' . strtoupper(substr($hash, 4, 4)); + } + } + catch (\Throwable $e) {} $this->recentLogins = $model->getRecentLogins(5); $this->pendingUpdates = $model->getPendingUpdates(); $this->checkedOutItems = $model->getCheckedOutItems(); diff --git a/source/packages/com_mokosuiteclient/admin/tmpl/dashboard/default.php b/source/packages/com_mokosuiteclient/admin/tmpl/dashboard/default.php index 483da496..161f75b0 100644 --- a/source/packages/com_mokosuiteclient/admin/tmpl/dashboard/default.php +++ b/source/packages/com_mokosuiteclient/admin/tmpl/dashboard/default.php @@ -48,6 +48,12 @@ $categoryOrder = ['core', 'security', 'monitoring', 'content', 'tools', 'api']; MokoSuiteClient escape($siteInfo->mokosuiteclient_version); ?> + supportPin)): ?> +
+ Support PIN + escape($this->supportPin); ?> +
+
Joomla escape($siteInfo->joomla_version); ?> diff --git a/source/packages/mod_mokosuiteclient_cpanel/src/Dispatcher/Dispatcher.php b/source/packages/mod_mokosuiteclient_cpanel/src/Dispatcher/Dispatcher.php index ca8aa5f4..a3e5ffcf 100644 --- a/source/packages/mod_mokosuiteclient_cpanel/src/Dispatcher/Dispatcher.php +++ b/source/packages/mod_mokosuiteclient_cpanel/src/Dispatcher/Dispatcher.php @@ -47,7 +47,7 @@ class Dispatcher extends AbstractModuleDispatcher implements HelperFactoryAwareI $data['currentIp'] = $helper->getCurrentIp(); $data['ssl'] = $helper->getSslStatus(); - // Support PIN derived from health token + // Daily support PIN derived from health token + today's date (UTC) $data['supportPin'] = ''; try @@ -65,7 +65,9 @@ class Dispatcher extends AbstractModuleDispatcher implements HelperFactoryAwareI if (!empty($token)) { - $data['supportPin'] = 'MOKO-' . strtoupper(substr($token, 0, 4) . '-' . substr($token, 4, 4)); + $date = gmdate('Y-m-d'); + $hash = hash_hmac('sha256', $date, $token); + $data['supportPin'] = 'MOKO-' . strtoupper(substr($hash, 0, 4)) . '-' . strtoupper(substr($hash, 4, 4)); } } catch (\Throwable $e) {}