142 lines
4.0 KiB
TypeScript
142 lines
4.0 KiB
TypeScript
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
import { GlobalPreferencesService, LoggerService, TokenService, UserService } from '../services/index.js';
|
|
import { ACCESS_TOKEN_COOKIE_NAME, MEET_API_KEY, PARTICIPANT_TOKEN_COOKIE_NAME } from '../environment.js';
|
|
import { container } from '../config/dependency-injector.config.js';
|
|
import { ClaimGrants } from 'livekit-server-sdk';
|
|
import { AuthMode, UserRole } from '@typings-ce';
|
|
import {
|
|
errorUnauthorized,
|
|
errorInvalidToken,
|
|
errorInvalidTokenSubject,
|
|
errorInsufficientPermissions,
|
|
errorInvalidApiKey,
|
|
OpenViduMeetError
|
|
} from '../models/index.js';
|
|
|
|
export const withAuth = (...validators: ((req: Request) => Promise<void>)[]): RequestHandler => {
|
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
let lastError: OpenViduMeetError | null = null;
|
|
|
|
for (const validator of validators) {
|
|
try {
|
|
await validator(req);
|
|
// If any middleware granted access, it is not necessary to continue checking the rest
|
|
return next();
|
|
} catch (error) {
|
|
// If no middleware granted access, return unauthorized
|
|
if (error instanceof OpenViduMeetError) {
|
|
lastError = error;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lastError) {
|
|
return res.status(lastError.statusCode).json({ message: lastError.message });
|
|
}
|
|
|
|
return res.status(500).json({ message: 'Internal server error' });
|
|
};
|
|
};
|
|
|
|
// Configure token validatior for role-based access
|
|
export const tokenAndRoleValidator = (role: UserRole) => {
|
|
return async (req: Request) => {
|
|
const token = req.cookies[ACCESS_TOKEN_COOKIE_NAME];
|
|
|
|
if (!token) {
|
|
throw errorUnauthorized();
|
|
}
|
|
|
|
const tokenService = container.get(TokenService);
|
|
let payload: ClaimGrants;
|
|
|
|
try {
|
|
payload = await tokenService.verifyToken(token);
|
|
} catch (error) {
|
|
throw errorInvalidToken();
|
|
}
|
|
|
|
const username = payload.sub;
|
|
const userService = container.get(UserService);
|
|
const user = username ? await userService.getUser(username) : null;
|
|
|
|
if (!user) {
|
|
throw errorInvalidTokenSubject();
|
|
}
|
|
|
|
if (user.role !== role) {
|
|
throw errorInsufficientPermissions();
|
|
}
|
|
|
|
req.session = req.session || {};
|
|
req.session.user = user;
|
|
};
|
|
};
|
|
|
|
// Configure token validatior for participant access
|
|
export const participantTokenValidator = async (req: Request) => {
|
|
const token = req.cookies[PARTICIPANT_TOKEN_COOKIE_NAME];
|
|
|
|
if (!token) {
|
|
throw errorUnauthorized();
|
|
}
|
|
|
|
const tokenService = container.get(TokenService);
|
|
|
|
try {
|
|
const payload = await tokenService.verifyToken(token);
|
|
req.session = req.session || {};
|
|
req.session.tokenClaims = payload;
|
|
} catch (error) {
|
|
throw errorInvalidToken();
|
|
}
|
|
};
|
|
|
|
// Configure API key validatior
|
|
export const apiKeyValidator = async (req: Request) => {
|
|
const apiKey = req.headers['x-api-key'];
|
|
|
|
if (!apiKey) {
|
|
throw errorUnauthorized();
|
|
}
|
|
|
|
if (apiKey !== MEET_API_KEY) {
|
|
throw errorInvalidApiKey();
|
|
}
|
|
};
|
|
|
|
// Allow anonymous access
|
|
export const allowAnonymous = async (req: Request) => {
|
|
const anonymousUser = {
|
|
username: 'anonymous',
|
|
role: UserRole.USER
|
|
};
|
|
|
|
req.session = req.session || {};
|
|
req.session.user = anonymousUser;
|
|
};
|
|
|
|
export const configureProfileAuth = async (req: Request, res: Response, next: NextFunction) => {
|
|
const logger = container.get(LoggerService);
|
|
const globalPrefService = container.get(GlobalPreferencesService);
|
|
let requireAuthForRoomCreation: boolean;
|
|
let authMode: AuthMode;
|
|
|
|
try {
|
|
const { securityPreferences } = await globalPrefService.getGlobalPreferences();
|
|
requireAuthForRoomCreation = securityPreferences.roomCreationPolicy.requireAuthentication;
|
|
authMode = securityPreferences.authentication.authMode;
|
|
} catch (error) {
|
|
logger.error('Error checking authentication preferences:' + error);
|
|
return res.status(500).json({ message: 'Internal server error' });
|
|
}
|
|
|
|
const authValidators = [tokenAndRoleValidator(UserRole.ADMIN)];
|
|
|
|
if (requireAuthForRoomCreation || authMode !== AuthMode.NONE) {
|
|
authValidators.push(tokenAndRoleValidator(UserRole.USER));
|
|
}
|
|
|
|
return withAuth(...authValidators)(req, res, next);
|
|
};
|