backend: Refactor authentication logic to use new global auth configuration
This commit is contained in:
parent
499e36786b
commit
042f7f2fd4
@ -20,7 +20,7 @@ export const login = async (req: Request, res: Response) => {
|
|||||||
const { username, password } = req.body as { username: string; password: string };
|
const { username, password } = req.body as { username: string; password: string };
|
||||||
|
|
||||||
const authService = container.get(AuthService);
|
const authService = container.get(AuthService);
|
||||||
const user = authService.authenticate(username, password);
|
const user = await authService.authenticate(username, password);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
logger.warn('Login failed');
|
logger.warn('Login failed');
|
||||||
@ -75,7 +75,7 @@ export const refreshToken = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const username = payload.sub;
|
const username = payload.sub;
|
||||||
const userService = container.get(UserService);
|
const userService = container.get(UserService);
|
||||||
const user = username ? userService.getUser(username) : null;
|
const user = username ? await userService.getUser(username) : null;
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
logger.warn('Invalid refresh token subject');
|
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) => {
|
export const getProfile = (req: Request, res: Response) => {
|
||||||
const user = req.session?.user;
|
const user = req.session?.user;
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.status(401).json({ message: 'Unauthorized' });
|
return res.status(401).json({ message: 'Unauthorized' });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,9 @@
|
|||||||
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
||||||
import { TokenService, UserService } from '../services/index.js';
|
import { GlobalPreferencesService, LoggerService, TokenService, UserService } from '../services/index.js';
|
||||||
import {
|
import { ACCESS_TOKEN_COOKIE_NAME, MEET_API_KEY, PARTICIPANT_TOKEN_COOKIE_NAME } from '../environment.js';
|
||||||
ACCESS_TOKEN_COOKIE_NAME,
|
|
||||||
MEET_API_KEY,
|
|
||||||
MEET_PRIVATE_ACCESS,
|
|
||||||
PARTICIPANT_TOKEN_COOKIE_NAME
|
|
||||||
} from '../environment.js';
|
|
||||||
import { container } from '../config/dependency-injector.config.js';
|
import { container } from '../config/dependency-injector.config.js';
|
||||||
import { ClaimGrants } from 'livekit-server-sdk';
|
import { ClaimGrants } from 'livekit-server-sdk';
|
||||||
import { Role } from '@typings-ce';
|
import { AuthMode, UserRole } from '@typings-ce';
|
||||||
import {
|
import {
|
||||||
errorUnauthorized,
|
errorUnauthorized,
|
||||||
errorInvalidToken,
|
errorInvalidToken,
|
||||||
@ -22,9 +17,9 @@ export const withAuth = (...validators: ((req: Request) => Promise<void>)[]): Re
|
|||||||
return async (req: Request, res: Response, next: NextFunction) => {
|
return async (req: Request, res: Response, next: NextFunction) => {
|
||||||
let lastError: OpenViduMeetError | null = null;
|
let lastError: OpenViduMeetError | null = null;
|
||||||
|
|
||||||
for (const middleware of validators) {
|
for (const validator of validators) {
|
||||||
try {
|
try {
|
||||||
await middleware(req);
|
await validator(req);
|
||||||
// If any middleware granted access, it is not necessary to continue checking the rest
|
// If any middleware granted access, it is not necessary to continue checking the rest
|
||||||
return next();
|
return next();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -44,13 +39,8 @@ export const withAuth = (...validators: ((req: Request) => Promise<void>)[]): Re
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Configure token validatior for role-based access
|
// Configure token validatior for role-based access
|
||||||
export const tokenAndRoleValidator = (role: Role) => {
|
export const tokenAndRoleValidator = (role: UserRole) => {
|
||||||
return async (req: Request) => {
|
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];
|
const token = req.cookies[ACCESS_TOKEN_COOKIE_NAME];
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
@ -68,7 +58,7 @@ export const tokenAndRoleValidator = (role: Role) => {
|
|||||||
|
|
||||||
const username = payload.sub;
|
const username = payload.sub;
|
||||||
const userService = container.get(UserService);
|
const userService = container.get(UserService);
|
||||||
const user = username ? userService.getUser(username) : null;
|
const user = username ? await userService.getUser(username) : null;
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw errorInvalidTokenSubject();
|
throw errorInvalidTokenSubject();
|
||||||
|
|||||||
@ -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 { 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 { UserService } from './user.service.js';
|
||||||
|
import { GlobalPreferencesService } from './preferences/global-preferences.service.js';
|
||||||
|
import { LoggerService } from './logger.service.js';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class AuthService {
|
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<User | null> {
|
||||||
const isAdmin = this.authenticateAdmin(username, password);
|
const isAdmin = this.authenticateAdmin(username, password);
|
||||||
const isUser = this.authenticateUser(username, password);
|
const isUser = await this.authenticateUser(username, password);
|
||||||
|
|
||||||
if (isAdmin || isUser) {
|
if (isAdmin || isUser) {
|
||||||
return this.userService.getUser(username);
|
return this.userService.getUser(username);
|
||||||
@ -22,9 +28,28 @@ export class AuthService {
|
|||||||
return username === MEET_ADMIN_USER && password === MEET_ADMIN_SECRET;
|
return username === MEET_ADMIN_USER && password === MEET_ADMIN_SECRET;
|
||||||
}
|
}
|
||||||
|
|
||||||
private authenticateUser(username: string, password: string): boolean {
|
private async authenticateUser(username: string, password: string): Promise<boolean> {
|
||||||
if (MEET_PRIVATE_ACCESS === 'true') {
|
let requireAuthForRoomCreation: boolean;
|
||||||
return username === MEET_USER && password === MEET_SECRET;
|
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;
|
return false;
|
||||||
|
|||||||
@ -1,21 +1,42 @@
|
|||||||
import { MEET_ADMIN_USER, MEET_USER } from '../environment.js';
|
import { MEET_ADMIN_USER } from '../environment.js';
|
||||||
import { injectable } from '../config/dependency-injector.config.js';
|
import { inject, injectable } from '../config/dependency-injector.config.js';
|
||||||
import { Role, User } from '@typings-ce';
|
import { AuthType, UserRole, SingleUserAuth, User } from '@typings-ce';
|
||||||
|
import { LoggerService } from './logger.service.js';
|
||||||
|
import { GlobalPreferencesService } from './preferences/global-preferences.service.js';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
getUser(username: string): User | null {
|
constructor(
|
||||||
|
@inject(LoggerService) protected logger: LoggerService,
|
||||||
|
@inject(GlobalPreferencesService) protected globalPrefService: GlobalPreferencesService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async getUser(username: string): Promise<User | null> {
|
||||||
if (username === MEET_ADMIN_USER) {
|
if (username === MEET_ADMIN_USER) {
|
||||||
return {
|
return {
|
||||||
username: MEET_ADMIN_USER,
|
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 {
|
return {
|
||||||
username: MEET_USER,
|
username: configuredUsername,
|
||||||
role: Role.USER
|
role: UserRole.USER
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user