diff --git a/classes/plugin/AdminBaseController.php b/classes/plugin/AdminBaseController.php index 100b6eb0..6ce6df43 100644 --- a/classes/plugin/AdminBaseController.php +++ b/classes/plugin/AdminBaseController.php @@ -96,6 +96,8 @@ class AdminBaseController // Make sure that user is logged into admin. if (!$this->admin->authorize()) { + $this->respondUnauthorizedIfAjax(); + return false; } @@ -236,6 +238,31 @@ class AdminBaseController $this->close($response); } + /** + * Return a JSON 401 response when an unauthenticated request was clearly triggered via AJAX. + * + * @return void + */ + protected function respondUnauthorizedIfAjax(): void + { + $uri = $this->grav['uri'] ?? null; + $extension = $uri ? $uri->extension() : null; + $accept = $_SERVER['HTTP_ACCEPT'] ?? ''; + $requestedWith = $_SERVER['HTTP_X_REQUESTED_WITH'] ?? ''; + + $acceptsJson = is_string($accept) && (stripos($accept, 'application/json') !== false || stripos($accept, 'text/json') !== false); + $isAjax = ($extension === 'json') || $acceptsJson || (is_string($requestedWith) && strtolower($requestedWith) === 'xmlhttprequest'); + + if (!$isAjax) { + return; + } + + $this->sendJsonResponse([ + 'status' => 'unauthenticated', + 'message' => Admin::translate('PLUGIN_ADMIN.SESSION_EXPIRED_DESC') + ], 401); + } + /** * @param ResponseInterface $response * @return never-return diff --git a/themes/grav/app/utils/keepalive.js b/themes/grav/app/utils/keepalive.js index dc6e823b..2e24499e 100644 --- a/themes/grav/app/utils/keepalive.js +++ b/themes/grav/app/utils/keepalive.js @@ -26,6 +26,9 @@ class KeepAlive { return fetch(`${config.base_url_relative}/task${config.param_sep}keepAlive`, { credentials: 'same-origin', + headers: { + 'Accept': 'application/json' + }, method: 'post', body: data }) diff --git a/themes/grav/js/admin.min.js b/themes/grav/js/admin.min.js index b7edd2f8..514c76c5 100644 --- a/themes/grav/js/admin.min.js +++ b/themes/grav/js/admin.min.js @@ -1064,6 +1064,9 @@ var KeepAlive = /*#__PURE__*/function () { data.append('admin-nonce', external_GravAdmin_namespaceObject.config.admin_nonce); return fetch("".concat(external_GravAdmin_namespaceObject.config.base_url_relative, "/task").concat(external_GravAdmin_namespaceObject.config.param_sep, "keepAlive"), { credentials: 'same-origin', + headers: { + 'Accept': 'application/json' + }, method: 'post', body: data }).then(function (response) {