From 9a2597a997539ac3cd09b7bdf286e9e015c4fe15 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Thu, 12 Feb 2026 08:07:21 +0100 Subject: [PATCH] frontend: enhance E2EE key handling and storage management --- .../meeting/guards/extract-params.guard.ts | 14 +++++-- .../services/meeting-context.service.ts | 37 ++++++++++++------- .../services/meeting-event-handler.service.ts | 6 --- .../meeting/services/meeting-lobby.service.ts | 6 ++- .../services/session-storage.service.ts | 23 ++++++------ 5 files changed, 49 insertions(+), 37 deletions(-) diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/guards/extract-params.guard.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/guards/extract-params.guard.ts index 28712501..eaee5632 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/guards/extract-params.guard.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/guards/extract-params.guard.ts @@ -2,7 +2,7 @@ import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivateFn } from '@angular/router'; import { NavigationService } from '../../../shared/services/navigation.service'; import { SessionStorageService } from '../../../shared/services/session-storage.service'; -import { extractParams, handleLeaveRedirectUrl } from '../../../shared/utils/url-params.utils'; +import { extractParams } from '../../../shared/utils/url-params.utils'; import { RoomMemberContextService } from '../../room-members/services/room-member-context.service'; import { MeetingContextService } from '../services/meeting-context.service'; @@ -21,7 +21,6 @@ export const extractRoomParamsGuard: CanActivateFn = (route: ActivatedRouteSnaps e2eeKey: queryE2eeKey } = extractParams(route); const secret = querySecret || sessionStorageService.getRoomSecret(); - const e2eeKey = queryE2eeKey || sessionStorageService.getE2EEKey(); // Handle leave redirect URL logic navigationService.handleLeaveRedirectUrl(leaveRedirectUrl); @@ -31,9 +30,16 @@ export const extractRoomParamsGuard: CanActivateFn = (route: ActivatedRouteSnaps if (secret) { meetingContextService.setRoomSecret(secret, true); } - if (e2eeKey) { - meetingContextService.setE2eeKey(e2eeKey); + + // Handle E2EE key: prioritize query param, fallback to storage + if (queryE2eeKey) { + // E2EE key came from URL parameter + meetingContextService.setE2eeKey(queryE2eeKey, true); + } else { + // Try to load E2EE key from storage + meetingContextService.loadE2eeKeyFromStorage(); } + if (participantName) { roomMemberContextService.setParticipantName(participantName); } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-context.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-context.service.ts index e0533b38..9ad8b499 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-context.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-context.service.ts @@ -24,6 +24,7 @@ export class MeetingContextService { private readonly _roomId = signal(undefined); private readonly _meetingUrl = signal(''); private readonly _e2eeKey = signal(''); + private readonly _isE2eeKeyFromUrl = signal(false); private readonly _roomSecret = signal(undefined); private readonly _hasRecordings = signal(false); private readonly _meetingEndedBy = signal<'self' | 'other' | null>(null); @@ -56,6 +57,11 @@ export class MeetingContextService { */ readonly e2eeKey = this._e2eeKey.asReadonly(); + /** + * Readonly signal for whether the E2EE key came from a URL parameter + */ + readonly isE2eeKeyFromUrl = this._isE2eeKeyFromUrl.asReadonly(); + /** * Readonly signal for the room secret */ @@ -106,6 +112,11 @@ export class MeetingContextService { */ readonly allowLayoutSwitching = computed(() => this.roomFeatureService.features().allowLayoutSwitching); + /** + * Computed signal for captions status based on room and global configuration + */ + readonly getCaptionsStatus = computed(() => this.roomFeatureService.features().captionsStatus); + /** * Computed signal for whether the device is mobile */ @@ -174,26 +185,23 @@ export class MeetingContextService { /** * Stores the E2EE key in context * @param key The E2EE key + * @param fromUrl Whether the key came from a URL parameter (default: false) */ - setE2eeKey(key: string): void { - this.sessionStorageService.setE2EEKey(key); + setE2eeKey(key: string, fromUrl = false): void { + this.sessionStorageService.setE2EEData(key, fromUrl); this._e2eeKey.set(key); + this._isE2eeKeyFromUrl.set(fromUrl); } /** - * Returns whether E2EE is enabled (has a key set) - * @returns true if E2EE is enabled, false otherwise + * Loads the E2EE key data from session storage */ - isE2eeEnabled(): boolean { - return this._e2eeKey().length > 0; - } - - /** - * Returns the captions status based on room and global configuration - * @returns CaptionsStatus ('HIDDEN' | 'ENABLED' | 'DISABLED_WITH_WARNING') - */ - getCaptionsStatus() { - return this.roomFeatureService.features().captionsStatus; + loadE2eeKeyFromStorage(): void { + const e2eeData = this.sessionStorageService.getE2EEData(); + if (e2eeData) { + this._e2eeKey.set(e2eeData.key); + this._isE2eeKeyFromUrl.set(e2eeData.fromUrl); + } } /** @@ -242,6 +250,7 @@ export class MeetingContextService { this._roomId.set(undefined); this._meetingUrl.set(''); this._e2eeKey.set(''); + this._isE2eeKeyFromUrl.set(false); this._roomSecret.set(undefined); this._hasRecordings.set(false); this._meetingEndedBy.set(null); diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-event-handler.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-event-handler.service.ts index 6637e2e7..6a970f7a 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-event-handler.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-event-handler.service.ts @@ -160,12 +160,6 @@ export class MeetingEventHandlerService { // Clear participant identity and token this.roomMemberContextService.clearContext(); - // Clean up room secret and e2ee key (if any), except on browser unload) - if (event.reason !== ParticipantLeftReason.BROWSER_UNLOAD) { - this.sessionStorageService.removeRoomSecret(); - this.sessionStorageService.removeE2EEKey(); - } - // Navigate to disconnected page await this.navigationService.navigateTo('disconnected', { reason: leftReason }, true); }; diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-lobby.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-lobby.service.ts index 8542b248..7df0fa22 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-lobby.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-lobby.service.ts @@ -175,8 +175,10 @@ export class MeetingLobbyService { const contextE2eeKey = this.meetingContextService.e2eeKey(); if (contextE2eeKey) { this.setE2eeKey(contextE2eeKey); - // fill the e2eeKey form control if already set in context (e.g., from URL param) - form.get('e2eeKey')?.disable(); + // Disable input only if the E2EE key was originally provided via URL parameter + if (this.meetingContextService.isE2eeKeyFromUrl()) { + form.get('e2eeKey')?.disable(); + } } form.get('e2eeKey')?.updateValueAndValidity(); } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/session-storage.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/session-storage.service.ts index b10eec60..8e8b863b 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/session-storage.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/session-storage.service.ts @@ -10,7 +10,7 @@ import { Injectable } from '@angular/core'; export class SessionStorageService { private readonly ROOM_SECRET_KEY = 'ovMeet-roomSecret'; private readonly REDIRECT_URL_KEY = 'ovMeet-redirectUrl'; - private readonly E2EE_KEY = 'ovMeet-e2eeKey'; + private readonly E2EE_DATA_KEY = 'ovMeet-e2eeData'; /** * Stores the room secret. @@ -56,28 +56,29 @@ export class SessionStorageService { } /** - * Stores the E2EE key. + * Stores the E2EE key data (key and origin flag). * * @param e2eeKey The E2EE key to store. + * @param fromUrl True if the E2EE key came from a URL parameter. */ - public setE2EEKey(e2eeKey: string): void { - this.set(this.E2EE_KEY, e2eeKey); + public setE2EEData(e2eeKey: string, fromUrl: boolean): void { + this.set(this.E2EE_DATA_KEY, { key: e2eeKey, fromUrl }); } /** - * Retrieves the E2EE key. + * Retrieves the E2EE key data (key and origin flag). * - * @returns The stored E2EE key or null if not found. + * @returns The stored E2EE data or null if not found. */ - public getE2EEKey(): string | null { - return this.get(this.E2EE_KEY); + public getE2EEData(): { key: string; fromUrl: boolean } | null { + return this.get<{ key: string; fromUrl: boolean }>(this.E2EE_DATA_KEY); } /** - * Removes the E2EE key. + * Removes the E2EE key data. */ - public removeE2EEKey(): void { - this.remove(this.E2EE_KEY); + public removeE2EEData(): void { + this.remove(this.E2EE_DATA_KEY); } /**