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 <antobinary@users.noreply.github.com>

---------

Co-authored-by: Anton Georgiev <antobinary@users.noreply.github.com>
This commit is contained in:
Gustavo Trott 2024-10-24 22:17:05 -03:00 committed by GitHub
parent 03a6901e52
commit 63bf4f35bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 51 additions and 4 deletions

View File

@ -284,7 +284,7 @@ const MeetingEnded: React.FC<MeetingEndedProps> = ({
{
learningDashboardAccessToken && isModerator
// Always set cookie in case Dashboard is already opened
&& setLearningDashboardCookie(learningDashboardAccessToken, meetingId) === true
&& setLearningDashboardCookie(learningDashboardAccessToken, meetingId, learningDashboardBase) === true
? (
<Styled.Text>
<Styled.MeetingEndedButton

View File

@ -24,6 +24,43 @@ export const MeetingEndedTable = {
ENDED_DUE_TO_SERVICE_INTERRUPTION: 'ENDED_DUE_TO_SERVICE_INTERRUPTION',
};
const findCommonDomain = (url1: string, url2: string): string => {
// 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;