From a692f8e37f08c28d3657dac40df99d029c52a48c Mon Sep 17 00:00:00 2001 From: juancarmore Date: Thu, 13 Mar 2025 15:04:59 +0100 Subject: [PATCH] backend: Implement participant token handling with cookie management and validation middleware --- .../src/controllers/participant.controller.ts | 5 ++-- backend/src/environment.ts | 4 ++- backend/src/middlewares/auth.middleware.ts | 28 ++++++++++++++++++- backend/src/services/livekit.service.ts | 10 +++++-- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/backend/src/controllers/participant.controller.ts b/backend/src/controllers/participant.controller.ts index 2e7184e..2b15871 100644 --- a/backend/src/controllers/participant.controller.ts +++ b/backend/src/controllers/participant.controller.ts @@ -5,6 +5,8 @@ import { TokenOptions } from '@typings-ce'; import { OpenViduMeetError } from '../models/index.js'; import { ParticipantService } from '../services/participant.service.js'; import { RoomService } from '../services/room.service.js'; +import { MEET_PARTICIPANT_TOKEN_EXPIRATION, PARTICIPANT_TOKEN_COOKIE_NAME } from '../environment.js'; +import { getCookieOptions } from '../utils/cookie-utils.js'; export const generateParticipantToken = async (req: Request, res: Response) => { const logger = container.get(LoggerService); @@ -28,8 +30,7 @@ export const generateParticipantToken = async (req: Request, res: Response) => { const secretRole = await roomService.getRoomSecretRole(roomName, secret); const token = await participantService.generateParticipantToken(secretRole, tokenOptions); - // TODO: Set the participant token in a cookie - // res.cookie('ovParticipantToken', token, { httpOnly: true, expires: tokenTtl }); + res.cookie(PARTICIPANT_TOKEN_COOKIE_NAME, token, getCookieOptions('/', MEET_PARTICIPANT_TOKEN_EXPIRATION)); logger.verbose(`Participant token generated for room ${roomName}`); return res.status(200).json({ token }); diff --git a/backend/src/environment.ts b/backend/src/environment.ts index 36db6d5..1a58129 100644 --- a/backend/src/environment.ts +++ b/backend/src/environment.ts @@ -19,10 +19,11 @@ export const { MEET_SECRET = 'user', MEET_ADMIN_USER = 'admin', MEET_ADMIN_SECRET = 'admin', + MEET_PARTICIPANT_TOKEN_EXPIRATION = '6h', MEET_ACCESS_TOKEN_EXPIRATION = '2h', MEET_REFRESH_TOKEN_EXPIRATION = '1d', MEET_PREFERENCES_STORAGE_MODE = 's3', - MEET_WEBHOOK_ENABLED = 'true', + MEET_WEBHOOK_ENABLED = 'false', MEET_WEBHOOK_URL = 'http://localhost:5080/webhook', MEET_LOG_LEVEL = 'info', @@ -60,6 +61,7 @@ export const { export const MEET_API_BASE_PATH = '/meet/api'; export const MEET_API_BASE_PATH_V1 = MEET_API_BASE_PATH + '/v1'; +export const PARTICIPANT_TOKEN_COOKIE_NAME = 'OvMeetParticipantToken'; export const ACCESS_TOKEN_COOKIE_NAME = 'OvMeetAccessToken'; export const REFRESH_TOKEN_COOKIE_NAME = 'OvMeetRefreshToken'; diff --git a/backend/src/middlewares/auth.middleware.ts b/backend/src/middlewares/auth.middleware.ts index b474c6f..38b429f 100644 --- a/backend/src/middlewares/auth.middleware.ts +++ b/backend/src/middlewares/auth.middleware.ts @@ -8,7 +8,8 @@ import { MEET_API_KEY, MEET_PRIVATE_ACCESS, MEET_SECRET, - MEET_USER + MEET_USER, + PARTICIPANT_TOKEN_COOKIE_NAME } from '../environment.js'; import { container } from '../config/dependency-injector.config.js'; @@ -35,6 +36,31 @@ export const withAdminValidToken = async (req: Request, res: Response, next: Nex next(); }; +export const withParticipantValidToken = async (req: Request, res: Response, next: NextFunction) => { + const token = req.cookies[PARTICIPANT_TOKEN_COOKIE_NAME]; + + if (!token) { + return res.status(401).json({ message: 'Unauthorized' }); + } + + const tokenService = container.get(TokenService); + + try { + const payload = await tokenService.verifyToken(token); + + // Parse metadata if it exists and add payload to request body for further processing + if (payload.metadata) { + payload.metadata = JSON.parse(payload.metadata); + } + + req.body.payload = payload; + } catch (error) { + return res.status(401).json({ message: 'Invalid token' }); + } + + next(); +}; + export const withValidApiKey = async (req: Request, res: Response, next: NextFunction) => { const apiKey = req.headers['x-api-key']; diff --git a/backend/src/services/livekit.service.ts b/backend/src/services/livekit.service.ts index 1365365..e8466f8 100644 --- a/backend/src/services/livekit.service.ts +++ b/backend/src/services/livekit.service.ts @@ -14,7 +14,13 @@ import { SendDataOptions, StreamOutput } from 'livekit-server-sdk'; -import { LIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_URL, LIVEKIT_URL_PRIVATE } from '../environment.js'; +import { + LIVEKIT_API_KEY, + LIVEKIT_API_SECRET, + LIVEKIT_URL, + LIVEKIT_URL_PRIVATE, + MEET_PARTICIPANT_TOKEN_EXPIRATION +} from '../environment.js'; import { LoggerService } from './logger.service.js'; import { errorLivekitIsNotAvailable, @@ -142,7 +148,7 @@ export class LiveKitService { const at = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET, { identity: participantName, name: participantName, - ttl: '24h', + ttl: MEET_PARTICIPANT_TOKEN_EXPIRATION, metadata: JSON.stringify({ livekitUrl: LIVEKIT_URL, role,