From 75fd019068c674cfdca7ec46bea80b68dd16f712 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Aug 2023 14:43:52 +0300 Subject: [PATCH] ClientTokenRotation: Rotate only expired tokens (#74010) * ClientTokenRotation: Rotate only expired tokens * Don't expose getSessionExpiry --- public/app/core/services/backend_srv.ts | 8 +++++++- public/app/core/services/context_srv.ts | 21 ++++----------------- public/app/core/utils/auth.ts | 13 +++++++++++++ 3 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 public/app/core/utils/auth.ts diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index 994e9613815..2d4245b72eb 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -20,6 +20,7 @@ import { GrafanaEdition } from '@grafana/data/src/types/config'; import { BackendSrv as BackendService, BackendSrvRequest, config, FetchError, FetchResponse } from '@grafana/runtime'; import appEvents from 'app/core/app_events'; import { getConfig } from 'app/core/config'; +import { getSessionExpiry } from 'app/core/utils/auth'; import { loadUrlToken } from 'app/core/utils/urlToken'; import { DashboardModel } from 'app/features/dashboard/state'; import { DashboardSearchItem } from 'app/features/search/types'; @@ -389,7 +390,12 @@ export class BackendSrv implements BackendService { return throwError(() => error); } - let authChecker = config.featureToggles.clientTokenRotation ? this.rotateToken() : this.loginPing(); + let authChecker = this.loginPing(); + + const expired = getSessionExpiry() * 1000 < Date.now(); + if (config.featureToggles.clientTokenRotation && expired) { + authChecker = this.rotateToken(); + } return from(authChecker).pipe( catchError((err) => { diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts index 1c902aa98cf..20b4697d676 100644 --- a/public/app/core/services/context_srv.ts +++ b/public/app/core/services/context_srv.ts @@ -2,6 +2,7 @@ import { extend } from 'lodash'; import { AnalyticsSettings, OrgRole, rangeUtil, WithAccessControlMetadata } from '@grafana/data'; import { featureEnabled, getBackendSrv } from '@grafana/runtime'; +import { getSessionExpiry } from 'app/core/utils/auth'; import { AccessControlAction, UserPermission } from 'app/types'; import { CurrentUserInternal } from 'app/types/config'; @@ -209,7 +210,7 @@ export class ContextSrv { // check if we can schedula the token rotation job if (this.canScheduleRotation()) { // get the time token is going to expire - let expires = this.getSessionExpiry(); + let expires = getSessionExpiry(); // because this job is scheduled for every tab we have open that shares a session we try // to distribute the scheduling of the job. For now this can be between 1 and 20 seconds @@ -222,7 +223,7 @@ export class ContextSrv { this.tokenRotationJobId = setTimeout(() => { // if we have a new expiry time from the expiry cookie another tab have already performed the rotation // so the only thing we need to do is reschedule the job and exit - if (this.getSessionExpiry() > expires) { + if (getSessionExpiry() > expires) { this.scheduleTokenRotationJob(); return; } @@ -247,7 +248,7 @@ export class ContextSrv { // from an older version of grafana, we never schedule the job and the fallback logic // in backend_srv will take care of rotations until first rotation has been made and // page has been reloaded. - if (this.getSessionExpiry() === 0) { + if (getSessionExpiry() === 0) { return false; } @@ -278,20 +279,6 @@ export class ContextSrv { console.error(e); }); } - - private getSessionExpiry() { - const expiryCookie = document.cookie.split('; ').find((row) => row.startsWith('grafana_session_expiry=')); - if (!expiryCookie) { - return 0; - } - - let expiresStr = expiryCookie.split('=').at(1); - if (!expiresStr) { - return 0; - } - - return parseInt(expiresStr, 10); - } } let contextSrv = new ContextSrv(); diff --git a/public/app/core/utils/auth.ts b/public/app/core/utils/auth.ts new file mode 100644 index 00000000000..42d99cd59c9 --- /dev/null +++ b/public/app/core/utils/auth.ts @@ -0,0 +1,13 @@ +export function getSessionExpiry() { + const expiryCookie = document.cookie.split('; ').find((row) => row.startsWith('grafana_session_expiry=')); + if (!expiryCookie) { + return 0; + } + + let expiresStr = expiryCookie.split('=').at(1); + if (!expiresStr) { + return 0; + } + + return parseInt(expiresStr, 10); +}