From b2f1e2194aed3c00a550edb31ceefdcd7685f9fe Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Mon, 9 Jun 2025 13:00:36 +0200 Subject: [PATCH] frontend: enhance participant token management and update related services - Renamed `setParticipantToken` to `setParticipantTokenAndUpdateContext` in ContextService to clarify its functionality. - Introduced ParticipantTokenService to encapsulate token generation logic and manage role/permissions extraction. - Updated VideoRoomComponent to utilize the new ParticipantTokenService for generating participant tokens. - Refactored access room method to improve form validation and participant name initialization. - Added unit tests for ParticipantTokenService to ensure proper functionality. - Updated sidenav model comments for clarity. --- .../src/lib/interceptors/http.interceptor.ts | 4 +- .../src/lib/models/auth.model.ts | 7 ++ .../src/lib/models/sidenav.model.ts | 8 +- .../video-room/video-room.component.html | 2 +- .../pages/video-room/video-room.component.ts | 76 ++++++++++++------- .../lib/services/context/context.service.ts | 10 ++- .../src/lib/services/index.ts | 1 + .../participant-token.service.spec.ts | 16 ++++ .../participant-token.service.ts | 33 ++++++++ 9 files changed, 118 insertions(+), 39 deletions(-) create mode 100644 frontend/projects/shared-meet-components/src/lib/models/auth.model.ts create mode 100644 frontend/projects/shared-meet-components/src/lib/services/participant-token/participant-token.service.spec.ts create mode 100644 frontend/projects/shared-meet-components/src/lib/services/participant-token/participant-token.service.ts diff --git a/frontend/projects/shared-meet-components/src/lib/interceptors/http.interceptor.ts b/frontend/projects/shared-meet-components/src/lib/interceptors/http.interceptor.ts index bbb20a7..828dd1d 100644 --- a/frontend/projects/shared-meet-components/src/lib/interceptors/http.interceptor.ts +++ b/frontend/projects/shared-meet-components/src/lib/interceptors/http.interceptor.ts @@ -53,7 +53,7 @@ export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest, ne return from(httpService.refreshParticipantToken({ roomId, participantName, secret })).pipe( switchMap((data) => { console.log('Participant token refreshed'); - contextService.setParticipantToken(data.token); + contextService.setParticipantTokenAndUpdateContext(data.token); return next(req); }), catchError((error: HttpErrorResponse) => { @@ -127,7 +127,7 @@ export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest, ne // refresh the participant token return refreshParticipantToken(error); } - + // Expired access token if (!pageUrl.startsWith('/console/login') && !pageUrl.startsWith('/login')) { // If the error occurred in a page that is not the login page, refresh the access token diff --git a/frontend/projects/shared-meet-components/src/lib/models/auth.model.ts b/frontend/projects/shared-meet-components/src/lib/models/auth.model.ts new file mode 100644 index 0000000..3186253 --- /dev/null +++ b/frontend/projects/shared-meet-components/src/lib/models/auth.model.ts @@ -0,0 +1,7 @@ +import { OpenViduMeetPermissions, ParticipantRole } from 'shared-meet-components'; + +export interface TokenGenerationResult { + token: string; // The generated participant token + role: ParticipantRole; // Role of the participant (e.g., 'moderator', 'publisher') + permissions: OpenViduMeetPermissions; // List of permissions granted to the participant +} diff --git a/frontend/projects/shared-meet-components/src/lib/models/sidenav.model.ts b/frontend/projects/shared-meet-components/src/lib/models/sidenav.model.ts index 531dc70..a46db69 100644 --- a/frontend/projects/shared-meet-components/src/lib/models/sidenav.model.ts +++ b/frontend/projects/shared-meet-components/src/lib/models/sidenav.model.ts @@ -1,6 +1,6 @@ export interface ConsoleNavLink { - label: string; // Nombre del enlace - icon?: string; // Icono opcional - route?: string; // Ruta para la navegación (opcional) - clickHandler?: () => void; // Función para manejar clics (opcional) + label: string; // Link name + icon?: string; // Optional icon + route?: string; // Route for navigation (optional) + clickHandler?: () => void; // Function to handle clicks (optional) } diff --git a/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.html b/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.html index 7297a84..f621b0f 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.html +++ b/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.html @@ -5,7 +5,7 @@

Access room {{ roomId }}

-
+ Name } A promise that resolves when the participant name has been initialized + */ + private async initializeParticipantName() { + // Apply participant name from context if set, otherwise use authenticated username + const contextParticipantName = this.ctxService.getParticipantName(); + const username = await this.authService.getUsername(); + const participantName = contextParticipantName || username; + + if (participantName) { + this.participantForm.get('name')?.setValue(participantName); + } + } + + /** + * Generates a participant token for joining a video room. + * + * @throws {Error} When participant already exists in the room (status 409) + * @returns {Promise} Promise that resolves when token is generated and set, or rejects on participant conflict + */ private async generateParticipantToken() { try { - const response = await this.httpService.generateParticipantToken({ - roomId: this.roomId, - participantName: this.participantName, - secret: this.roomSecret - }); - this.setParticipantToken(response.token); + const { token, role, permissions } = await this.participantTokenService.generateToken( + this.roomId, + this.participantName, + this.roomSecret + ); + this.participantToken = token; + this.participantRole = role; + this.participantPermissions = permissions; } catch (error: any) { console.error('Error generating participant token:', error); switch (error.status) { @@ -166,16 +194,6 @@ export class VideoRoomComponent implements OnInit, OnDestroy { } } - private setParticipantToken(token: string): void { - try { - this.ctxService.setParticipantToken(token); - this.participantRole = this.ctxService.getParticipantRole(); - this.participantPermissions = this.ctxService.getParticipantPermissions(); - } catch (error: any) { - console.error('Error setting token in context', error); - } - } - private async replaceUrlQueryParams() { let secretQueryParam = this.roomSecret; diff --git a/frontend/projects/shared-meet-components/src/lib/services/context/context.service.ts b/frontend/projects/shared-meet-components/src/lib/services/context/context.service.ts index 0ebeff4..64a7779 100644 --- a/frontend/projects/shared-meet-components/src/lib/services/context/context.service.ts +++ b/frontend/projects/shared-meet-components/src/lib/services/context/context.service.ts @@ -119,15 +119,19 @@ export class ContextService { } /** - * Sets the token for the current session. - * @param token - A string representing the token. + * Sets the participant token in the context and updates feature configuration. + * @param token - The JWT token to set. + * @throws Error if the token is invalid or expired. */ - setParticipantToken(token: string): void { + setParticipantTokenAndUpdateContext(token: string): void { try { const decodedToken = this.getValidDecodedToken(token); this.context.participantToken = token; this.context.participantPermissions = decodedToken.metadata.permissions; this.context.participantRole = decodedToken.metadata.role; + + // Update feature configuration based on the new token + this.updateFeatureConfiguration(); } catch (error: any) { this.log.e('Error setting token in context', error); throw new Error('Error setting token', error); diff --git a/frontend/projects/shared-meet-components/src/lib/services/index.ts b/frontend/projects/shared-meet-components/src/lib/services/index.ts index 66a680f..bb81984 100644 --- a/frontend/projects/shared-meet-components/src/lib/services/index.ts +++ b/frontend/projects/shared-meet-components/src/lib/services/index.ts @@ -6,3 +6,4 @@ export * from './room/room.service'; export * from './notification/notification.service'; export * from './webcomponent-manager/webcomponent-manager.service'; export * from './session-storage/session-storage.service'; +export * from './participant-token/participant-token.service'; diff --git a/frontend/projects/shared-meet-components/src/lib/services/participant-token/participant-token.service.spec.ts b/frontend/projects/shared-meet-components/src/lib/services/participant-token/participant-token.service.spec.ts new file mode 100644 index 0000000..025001a --- /dev/null +++ b/frontend/projects/shared-meet-components/src/lib/services/participant-token/participant-token.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ParticipantTokenService } from './participant-token.service'; + +describe('ParticipantTokenService', () => { + let service: ParticipantTokenService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ParticipantTokenService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/projects/shared-meet-components/src/lib/services/participant-token/participant-token.service.ts b/frontend/projects/shared-meet-components/src/lib/services/participant-token/participant-token.service.ts new file mode 100644 index 0000000..68c938d --- /dev/null +++ b/frontend/projects/shared-meet-components/src/lib/services/participant-token/participant-token.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { TokenGenerationResult } from '@lib/models/auth.model'; +import { HttpService, ContextService, SessionStorageService } from 'shared-meet-components'; + +@Injectable({ + providedIn: 'root' +}) +export class ParticipantTokenService { + constructor( + private httpService: HttpService, + private ctxService: ContextService, + private sessionStorageService: SessionStorageService + ) {} + + /** + * Generates a participant token and extracts role/permissions + */ + async generateToken(roomId: string, participantName: string, secret: string): Promise { + const response = await this.httpService.generateParticipantToken({ + roomId, + participantName, + secret + }); + + this.ctxService.setParticipantTokenAndUpdateContext(response.token); + + return { + token: response.token, + role: this.ctxService.getParticipantRole(), + permissions: this.ctxService.getParticipantPermissions() + }; + } +}