diff --git a/pkg/services/frontend/index.html b/pkg/services/frontend/index.html
index 2eb4d82dce3..0a589f81a49 100644
--- a/pkg/services/frontend/index.html
+++ b/pkg/services/frontend/index.html
@@ -239,6 +239,30 @@
const CHECK_INTERVAL = 1 * 1000;
+ function getCookie(name) {
+ const cookies = document.cookie.split(";").map(c => c.trim());
+
+ for (const cookie of cookies) {
+ if (cookie.startsWith(name + "=")) {
+ return cookie.substring(name.length + 1);
+ }
+ }
+
+ return null;
+ }
+
+ function getSessionExpiration() {
+ const value = getCookie("grafana_session_expiry") || "0";
+ const realExpiresSeconds = parseInt(value, 10);
+ const expiresSeconds = Math.max(realExpiresSeconds - 10, 0); // Rotate 10s before the real expiration
+ const expiration = new Date(expiresSeconds * 1000);
+ return expiration;
+ }
+
+ async function rotateSession() {
+ await fetch('/api/user/auth-tokens/rotate', { method: 'POST' });
+ }
+
/**
* Fetches boot data from the server. If it returns undefined, it should be retried later.
* Will return a rejected promise on unrecoverable errors.
@@ -295,6 +319,19 @@
function loadBootData() {
return new Promise((resolve, reject) => {
const attemptFetch = async () => {
+ try {
+ const sessionExpiration = getSessionExpiration();
+ const now = new Date();
+
+ // If the session has expired, don't continue trying to fetch boot data
+ if (now >= sessionExpiration) {
+ await rotateSession();
+ }
+ } catch (error) {
+ // Just ignore any errors in session rotation. The user can just log in again.
+ console.warn("Failed to rotate session", error);
+ }
+
try {
const bootData = await fetchBootData();