From 63bf4f35bcce106c725f752351cb4c25f6f35683 Mon Sep 17 00:00:00 2001 From: Gustavo Trott Date: Thu, 24 Oct 2024 22:17:05 -0300 Subject: [PATCH] fix (LAD/cluster): Enable LearningDashboard cookie support across different subdomains (#21518) * Enable LearningDashboard cookie support across different subdomains. After a meeting ends, the client sets a cookie `ld-${meetingId}` with a token used to retrieve meeting data in JSON format. The Learning Dashboard needs access to this cookie to obtain the token. The issue occurs in a Cluster setup where the Dashboard is hosted on a different subdomain, preventing it from accessing the cookie. To resolve this, the client will set the cookie using the root domain (excluding the subdomain). This allows the cookie to be accessible from any subdomain. * lint fixes * Update bigbluebutton-html5/imports/ui/components/meeting-ended/service.ts Co-authored-by: Anton Georgiev --------- Co-authored-by: Anton Georgiev --- .../ui/components/meeting-ended/component.tsx | 2 +- .../ui/components/meeting-ended/service.ts | 53 +++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.tsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.tsx index 83baa00492..8ed738b21b 100644 --- a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.tsx +++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.tsx @@ -284,7 +284,7 @@ const MeetingEnded: React.FC = ({ { learningDashboardAccessToken && isModerator // Always set cookie in case Dashboard is already opened - && setLearningDashboardCookie(learningDashboardAccessToken, meetingId) === true + && setLearningDashboardCookie(learningDashboardAccessToken, meetingId, learningDashboardBase) === true ? ( { + // Helper function to extract domain parts in reverse order + const getDomainParts = (url: string): string[] => { + try { + const { hostname } = new URL(url); + return hostname.split('.').reverse(); + } catch (e) { + throw new Error(`Invalid URL format: ${url}`); + } + }; + + try { + const domain1Parts: string[] = getDomainParts(url1); + const domain2Parts: string[] = getDomainParts(url2); + + // Find common parts starting from the end (TLD) + const commonParts: string[] = []; + const minLength: number = Math.min(domain1Parts.length, domain2Parts.length); + + for (let i = 0; i < minLength; i += 1) { + if (domain1Parts[i] === domain2Parts[i]) { + commonParts.push(domain1Parts[i]); + } else { + break; + } + } + + // Return the common parts in correct order + if (commonParts.length > 0) { + return commonParts.reverse().join('.'); + } + return ''; + } catch (error) { + return ''; + } +}; + export const openLearningDashboardUrl = ( accessToken: string, mId: string, @@ -31,18 +68,28 @@ export const openLearningDashboardUrl = ( learningDashboardBase: string, lang: string, ) => { - if (accessToken && setLearningDashboardCookie(accessToken, mId)) { + if (accessToken && setLearningDashboardCookie(accessToken, mId, learningDashboardBase)) { window.open(`${learningDashboardBase}/?meeting=${mId}&lang=${lang}`, '_blank'); } else { window.open(`${learningDashboardBase}/?meeting=${mId}&sessionToken=${sToken}&lang=${lang}`, '_blank'); } }; -export const setLearningDashboardCookie = (accessToken: string, mId: string) => { +export const setLearningDashboardCookie = (accessToken: string, mId: string, learningDashboardBase: string) => { if (accessToken !== null) { const lifetime = new Date(); lifetime.setTime(lifetime.getTime() + (3600000)); // 1h (extends 7d when open Dashboard) - document.cookie = `ld-${mId}=${accessToken}; expires=${lifetime.toUTCString()}; path=/`; + let cookieString = `ld-${mId}=${accessToken}; expires=${lifetime.toUTCString()}; path=/`; + + // In a cluster setup it will be necessary to specify the root domain + // because the Dashboard might be in a different subdomain + if (learningDashboardBase && learningDashboardBase.startsWith('http')) { + const commonDomain = findCommonDomain(learningDashboardBase, window.location.href); + if (commonDomain !== '') { + cookieString += `;domain=${commonDomain}`; + } + } + document.cookie = cookieString; return true; } return false;