diff --git a/backend/src/controllers/auth.controller.ts b/backend/src/controllers/auth.controller.ts index c361989..9e5166c 100644 --- a/backend/src/controllers/auth.controller.ts +++ b/backend/src/controllers/auth.controller.ts @@ -20,7 +20,7 @@ export const login = async (req: Request, res: Response) => { const { username, password } = req.body as { username: string; password: string }; const authService = container.get(AuthService); - const user = authService.authenticate(username, password); + const user = await authService.authenticate(username, password); if (!user) { logger.warn('Login failed'); @@ -75,7 +75,7 @@ export const refreshToken = async (req: Request, res: Response) => { const username = payload.sub; const userService = container.get(UserService); - const user = username ? userService.getUser(username) : null; + const user = username ? await userService.getUser(username) : null; if (!user) { logger.warn('Invalid refresh token subject'); @@ -95,7 +95,7 @@ export const refreshToken = async (req: Request, res: Response) => { export const getProfile = (req: Request, res: Response) => { const user = req.session?.user; - + if (!user) { return res.status(401).json({ message: 'Unauthorized' }); } diff --git a/backend/src/middlewares/auth.middleware.ts b/backend/src/middlewares/auth.middleware.ts index 03614c4..2e38afb 100644 --- a/backend/src/middlewares/auth.middleware.ts +++ b/backend/src/middlewares/auth.middleware.ts @@ -1,14 +1,9 @@ import { NextFunction, Request, RequestHandler, Response } from 'express'; -import { TokenService, UserService } from '../services/index.js'; -import { - ACCESS_TOKEN_COOKIE_NAME, - MEET_API_KEY, - MEET_PRIVATE_ACCESS, - PARTICIPANT_TOKEN_COOKIE_NAME -} from '../environment.js'; +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 { Role } from '@typings-ce'; +import { AuthMode, UserRole } from '@typings-ce'; import { errorUnauthorized, errorInvalidToken, @@ -22,9 +17,9 @@ export const withAuth = (...validators: ((req: Request) => Promise)[]): Re return async (req: Request, res: Response, next: NextFunction) => { let lastError: OpenViduMeetError | null = null; - for (const middleware of validators) { + for (const validator of validators) { try { - await middleware(req); + await validator(req); // If any middleware granted access, it is not necessary to continue checking the rest return next(); } catch (error) { @@ -44,13 +39,8 @@ export const withAuth = (...validators: ((req: Request) => Promise)[]): Re }; // Configure token validatior for role-based access -export const tokenAndRoleValidator = (role: Role) => { +export const tokenAndRoleValidator = (role: UserRole) => { return async (req: Request) => { - // Skip token validation if role is USER and access is public - if (role == Role.USER && MEET_PRIVATE_ACCESS === 'false') { - return; - } - const token = req.cookies[ACCESS_TOKEN_COOKIE_NAME]; if (!token) { @@ -68,7 +58,7 @@ export const tokenAndRoleValidator = (role: Role) => { const username = payload.sub; const userService = container.get(UserService); - const user = username ? userService.getUser(username) : null; + const user = username ? await userService.getUser(username) : null; if (!user) { throw errorInvalidTokenSubject(); diff --git a/backend/src/services/auth.service.ts b/backend/src/services/auth.service.ts index f245af7..c151813 100644 --- a/backend/src/services/auth.service.ts +++ b/backend/src/services/auth.service.ts @@ -1,15 +1,21 @@ -import { MEET_ADMIN_SECRET, MEET_ADMIN_USER, MEET_PRIVATE_ACCESS, MEET_SECRET, MEET_USER } from '../environment.js'; +import { MEET_ADMIN_SECRET, MEET_ADMIN_USER } from '../environment.js'; import { inject, injectable } from '../config/dependency-injector.config.js'; -import { User } from '@typings-ce'; +import { AuthMode, AuthType, SingleUserAuth, User, ValidAuthMethod } from '@typings-ce'; import { UserService } from './user.service.js'; +import { GlobalPreferencesService } from './preferences/global-preferences.service.js'; +import { LoggerService } from './logger.service.js'; @injectable() export class AuthService { - constructor(@inject(UserService) protected userService: UserService) {} + constructor( + @inject(LoggerService) protected logger: LoggerService, + @inject(UserService) protected userService: UserService, + @inject(GlobalPreferencesService) protected globalPrefService: GlobalPreferencesService + ) {} - authenticate(username: string, password: string): User | null { + async authenticate(username: string, password: string): Promise { const isAdmin = this.authenticateAdmin(username, password); - const isUser = this.authenticateUser(username, password); + const isUser = await this.authenticateUser(username, password); if (isAdmin || isUser) { return this.userService.getUser(username); @@ -22,9 +28,28 @@ export class AuthService { return username === MEET_ADMIN_USER && password === MEET_ADMIN_SECRET; } - private authenticateUser(username: string, password: string): boolean { - if (MEET_PRIVATE_ACCESS === 'true') { - return username === MEET_USER && password === MEET_SECRET; + private async authenticateUser(username: string, password: string): Promise { + let requireAuthForRoomCreation: boolean; + let authMode: AuthMode; + let authMethod: ValidAuthMethod; + + try { + const { securityPreferences } = await this.globalPrefService.getGlobalPreferences(); + requireAuthForRoomCreation = securityPreferences.roomCreationPolicy.requireAuthentication; + ({ authMode, method: authMethod } = securityPreferences.authentication); + } catch (error) { + this.logger.error('Error checking authentication preferences:' + error); + return false; + } + + if (requireAuthForRoomCreation || authMode !== AuthMode.NONE) { + if (authMethod.type !== AuthType.SINGLE_USER) { + return false; + } + + const { username: configuredUsername, passwordHash: configurePassword } = (authMethod as SingleUserAuth) + .credentials; + return username === configuredUsername && password === configurePassword; } return false; diff --git a/backend/src/services/user.service.ts b/backend/src/services/user.service.ts index 0f72cdb..43cf6c7 100644 --- a/backend/src/services/user.service.ts +++ b/backend/src/services/user.service.ts @@ -1,21 +1,42 @@ -import { MEET_ADMIN_USER, MEET_USER } from '../environment.js'; -import { injectable } from '../config/dependency-injector.config.js'; -import { Role, User } from '@typings-ce'; +import { MEET_ADMIN_USER } from '../environment.js'; +import { inject, injectable } from '../config/dependency-injector.config.js'; +import { AuthType, UserRole, SingleUserAuth, User } from '@typings-ce'; +import { LoggerService } from './logger.service.js'; +import { GlobalPreferencesService } from './preferences/global-preferences.service.js'; @injectable() export class UserService { - getUser(username: string): User | null { + constructor( + @inject(LoggerService) protected logger: LoggerService, + @inject(GlobalPreferencesService) protected globalPrefService: GlobalPreferencesService + ) {} + + async getUser(username: string): Promise { if (username === MEET_ADMIN_USER) { return { username: MEET_ADMIN_USER, - role: Role.ADMIN + role: UserRole.ADMIN }; } - if (username === MEET_USER) { + let configuredUsername: string | undefined; + + try { + const { securityPreferences } = await this.globalPrefService.getGlobalPreferences(); + const method = securityPreferences.authentication.method; + + if (method.type === AuthType.SINGLE_USER) { + configuredUsername = (method as SingleUserAuth).credentials.username; + } + } catch (error) { + this.logger.error('Error checking room creation policy:' + error); + return null; + } + + if (username === configuredUsername) { return { - username: MEET_USER, - role: Role.USER + username: configuredUsername, + role: UserRole.USER }; }