backend: Implement participant token handling with cookie management and validation middleware

This commit is contained in:
juancarmore 2025-03-13 15:04:59 +01:00
parent e9882c19dc
commit a692f8e37f
4 changed files with 41 additions and 6 deletions

View File

@ -5,6 +5,8 @@ import { TokenOptions } from '@typings-ce';
import { OpenViduMeetError } from '../models/index.js'; import { OpenViduMeetError } from '../models/index.js';
import { ParticipantService } from '../services/participant.service.js'; import { ParticipantService } from '../services/participant.service.js';
import { RoomService } from '../services/room.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) => { export const generateParticipantToken = async (req: Request, res: Response) => {
const logger = container.get(LoggerService); 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 secretRole = await roomService.getRoomSecretRole(roomName, secret);
const token = await participantService.generateParticipantToken(secretRole, tokenOptions); const token = await participantService.generateParticipantToken(secretRole, tokenOptions);
// TODO: Set the participant token in a cookie res.cookie(PARTICIPANT_TOKEN_COOKIE_NAME, token, getCookieOptions('/', MEET_PARTICIPANT_TOKEN_EXPIRATION));
// res.cookie('ovParticipantToken', token, { httpOnly: true, expires: tokenTtl });
logger.verbose(`Participant token generated for room ${roomName}`); logger.verbose(`Participant token generated for room ${roomName}`);
return res.status(200).json({ token }); return res.status(200).json({ token });

View File

@ -19,10 +19,11 @@ export const {
MEET_SECRET = 'user', MEET_SECRET = 'user',
MEET_ADMIN_USER = 'admin', MEET_ADMIN_USER = 'admin',
MEET_ADMIN_SECRET = 'admin', MEET_ADMIN_SECRET = 'admin',
MEET_PARTICIPANT_TOKEN_EXPIRATION = '6h',
MEET_ACCESS_TOKEN_EXPIRATION = '2h', MEET_ACCESS_TOKEN_EXPIRATION = '2h',
MEET_REFRESH_TOKEN_EXPIRATION = '1d', MEET_REFRESH_TOKEN_EXPIRATION = '1d',
MEET_PREFERENCES_STORAGE_MODE = 's3', MEET_PREFERENCES_STORAGE_MODE = 's3',
MEET_WEBHOOK_ENABLED = 'true', MEET_WEBHOOK_ENABLED = 'false',
MEET_WEBHOOK_URL = 'http://localhost:5080/webhook', MEET_WEBHOOK_URL = 'http://localhost:5080/webhook',
MEET_LOG_LEVEL = 'info', MEET_LOG_LEVEL = 'info',
@ -60,6 +61,7 @@ export const {
export const MEET_API_BASE_PATH = '/meet/api'; export const MEET_API_BASE_PATH = '/meet/api';
export const MEET_API_BASE_PATH_V1 = MEET_API_BASE_PATH + '/v1'; 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 ACCESS_TOKEN_COOKIE_NAME = 'OvMeetAccessToken';
export const REFRESH_TOKEN_COOKIE_NAME = 'OvMeetRefreshToken'; export const REFRESH_TOKEN_COOKIE_NAME = 'OvMeetRefreshToken';

View File

@ -8,7 +8,8 @@ import {
MEET_API_KEY, MEET_API_KEY,
MEET_PRIVATE_ACCESS, MEET_PRIVATE_ACCESS,
MEET_SECRET, MEET_SECRET,
MEET_USER MEET_USER,
PARTICIPANT_TOKEN_COOKIE_NAME
} from '../environment.js'; } from '../environment.js';
import { container } from '../config/dependency-injector.config.js'; import { container } from '../config/dependency-injector.config.js';
@ -35,6 +36,31 @@ export const withAdminValidToken = async (req: Request, res: Response, next: Nex
next(); 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) => { export const withValidApiKey = async (req: Request, res: Response, next: NextFunction) => {
const apiKey = req.headers['x-api-key']; const apiKey = req.headers['x-api-key'];

View File

@ -14,7 +14,13 @@ import {
SendDataOptions, SendDataOptions,
StreamOutput StreamOutput
} from 'livekit-server-sdk'; } 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 { LoggerService } from './logger.service.js';
import { import {
errorLivekitIsNotAvailable, errorLivekitIsNotAvailable,
@ -142,7 +148,7 @@ export class LiveKitService {
const at = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET, { const at = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET, {
identity: participantName, identity: participantName,
name: participantName, name: participantName,
ttl: '24h', ttl: MEET_PARTICIPANT_TOKEN_EXPIRATION,
metadata: JSON.stringify({ metadata: JSON.stringify({
livekitUrl: LIVEKIT_URL, livekitUrl: LIVEKIT_URL,
role, role,