frontend: enhance E2EE key handling and storage management

This commit is contained in:
juancarmore 2026-02-12 08:07:21 +01:00
parent fb4bdbfcfb
commit 9a2597a997
5 changed files with 49 additions and 37 deletions

View File

@ -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);
}

View File

@ -24,6 +24,7 @@ export class MeetingContextService {
private readonly _roomId = signal<string | undefined>(undefined);
private readonly _meetingUrl = signal<string>('');
private readonly _e2eeKey = signal<string>('');
private readonly _isE2eeKeyFromUrl = signal<boolean>(false);
private readonly _roomSecret = signal<string | undefined>(undefined);
private readonly _hasRecordings = signal<boolean>(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);

View File

@ -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);
};

View File

@ -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();
}

View File

@ -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<string>(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);
}
/**