diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx index 0d003dd887f3cf..09172eb8bc1167 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx @@ -5,6 +5,8 @@ * 2.0. */ +import type { BroadcastChannel as BroadcastChannelType } from 'broadcast-channel'; +import { createLeaderElection } from 'broadcast-channel'; import { sortBy } from 'lodash'; import React from 'react'; import ReactDOM from 'react-dom'; @@ -41,7 +43,11 @@ export interface SecurityNavControlServiceStart { addUserMenuLinks: (newUserMenuLink: UserMenuLink[]) => void; } +const USER_IN_SESSION = 'user_in_session'; + export class SecurityNavControlService { + private channel?: BroadcastChannelType<{ userInSession: boolean }>; + private securityLicense!: SecurityLicense; private authc!: AuthenticationServiceSetup; private logoutUrl!: string; @@ -69,6 +75,7 @@ export class SecurityNavControlService { if (shouldRegisterNavControl) { this.registerSecurityNavControl(core); } + this.telemetryOnAuthType(core); } ); @@ -112,17 +119,7 @@ export class SecurityNavControlService { private registerSecurityNavControl( core: Pick ) { - const currentUserPromise = this.authc.getCurrentUser().then((authenticatedUser) => { - console.log('### only one time ###'); - try { - core.http.post('/internal/security/telemetry/auth_type', { - body: JSON.stringify({ auth_type: authenticatedUser.authentication_type }), - }); - // eslint-disable-next-line no-empty - } catch {} - - return authenticatedUser; - }); + const currentUserPromise = this.authc.getCurrentUser(); core.chrome.navControls.registerRight({ order: 2000, mount: (el: HTMLElement) => { @@ -151,4 +148,39 @@ export class SecurityNavControlService { private sortUserMenuLinks(userMenuLinks: UserMenuLink[]) { return sortBy(userMenuLinks, 'order'); } + + private async telemetryOnAuthType(core: Pick) { + try { + const { BroadcastChannel } = await import('broadcast-channel'); + const authenticatedUser = await this.authc.getCurrentUser(); + + if (!this.channel) { + const tenant = core.http.basePath.serverBasePath; + this.channel = new BroadcastChannel(`${tenant}/${USER_IN_SESSION}`, { + webWorkerSupport: false, + }); + } + + const elector = createLeaderElection(this.channel, { + fallbackInterval: 0, // optional configuration for how often will renegotiation for leader occur -> no need of renegotiation + responseTime: 1000, // optional configuration for how long will instances have to respond + }); + elector.awaitLeadership().then(() => { + if (elector.isLeader) { + core.http.post('/internal/security/telemetry/auth_type', { + body: JSON.stringify({ auth_type: authenticatedUser.authentication_type }), + }); + } + }); + + setTimeout(async () => { + if (!elector.isLeader) { + // Let's kill the non leader since we do not need to do telemetry if you are not a leader + await elector.die(); + } + }, 5000); + + // eslint-disable-next-line no-empty + } catch {} + } }