Refactors the internal configuration to be exported as a constant, improving code modularity and preventing accidental modifications. Updates all files that use the internal config to import the constant instead of the default export.
184 lines
5.8 KiB
TypeScript
184 lines
5.8 KiB
TypeScript
import { AuthTransportMode } from '@openvidu-meet/typings';
|
|
import { Request, Response } from 'express';
|
|
import { ClaimGrants } from 'livekit-server-sdk';
|
|
import { container } from '../config/index.js';
|
|
import { INTERNAL_CONFIG } from '../config/internal-config.js';
|
|
import {
|
|
errorInvalidCredentials,
|
|
errorInvalidRefreshToken,
|
|
errorInvalidTokenSubject,
|
|
errorRefreshTokenNotPresent,
|
|
handleError,
|
|
rejectRequestFromMeetError
|
|
} from '../models/error.model.js';
|
|
import { AuthService, LoggerService, TokenService, UserService } from '../services/index.js';
|
|
import { getAuthTransportMode, getCookieOptions, getRefreshToken } from '../utils/index.js';
|
|
|
|
export const login = async (req: Request, res: Response) => {
|
|
const logger = container.get(LoggerService);
|
|
logger.verbose('Login request received');
|
|
const { username, password } = req.body as { username: string; password: string };
|
|
|
|
const authService = container.get(AuthService);
|
|
const user = await authService.authenticateUser(username, password);
|
|
|
|
if (!user) {
|
|
logger.warn('Login failed');
|
|
const error = errorInvalidCredentials();
|
|
return rejectRequestFromMeetError(res, error);
|
|
}
|
|
|
|
try {
|
|
const tokenService = container.get(TokenService);
|
|
const accessToken = await tokenService.generateAccessToken(user);
|
|
const refreshToken = await tokenService.generateRefreshToken(user);
|
|
|
|
logger.info(`Login succeeded for user '${username}'`);
|
|
const transportMode = await getAuthTransportMode();
|
|
|
|
if (transportMode === AuthTransportMode.HEADER) {
|
|
// Send tokens in response body for header mode
|
|
return res.status(200).json({
|
|
message: `User '${username}' logged in successfully`,
|
|
accessToken,
|
|
refreshToken
|
|
});
|
|
} else {
|
|
// Send tokens as cookies for cookie mode
|
|
res.cookie(
|
|
INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME,
|
|
accessToken,
|
|
getCookieOptions('/', INTERNAL_CONFIG.ACCESS_TOKEN_EXPIRATION)
|
|
);
|
|
res.cookie(
|
|
INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME,
|
|
refreshToken,
|
|
getCookieOptions(
|
|
`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth`,
|
|
INTERNAL_CONFIG.REFRESH_TOKEN_EXPIRATION
|
|
)
|
|
);
|
|
return res.status(200).json({ message: `User '${username}' logged in successfully` });
|
|
}
|
|
} catch (error) {
|
|
handleError(res, error, 'generating access and refresh tokens');
|
|
}
|
|
};
|
|
|
|
export const logout = async (_req: Request, res: Response) => {
|
|
const transportMode = await getAuthTransportMode();
|
|
|
|
if (transportMode === AuthTransportMode.COOKIE) {
|
|
// Clear cookies only in cookie mode
|
|
res.clearCookie(INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
|
|
res.clearCookie(INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME, {
|
|
path: `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth`
|
|
});
|
|
}
|
|
|
|
// In header mode, the client is responsible for clearing localStorage
|
|
return res.status(200).json({ message: 'Logout successful' });
|
|
};
|
|
|
|
export const refreshToken = async (req: Request, res: Response) => {
|
|
const logger = container.get(LoggerService);
|
|
logger.verbose('Refresh token request received');
|
|
|
|
// Get refresh token from cookie or header based on transport mode
|
|
const refreshToken = await getRefreshToken(req);
|
|
|
|
if (!refreshToken) {
|
|
logger.warn('No refresh token provided');
|
|
const error = errorRefreshTokenNotPresent();
|
|
return rejectRequestFromMeetError(res, error);
|
|
}
|
|
|
|
const tokenService = container.get(TokenService);
|
|
let payload: ClaimGrants;
|
|
|
|
try {
|
|
payload = await tokenService.verifyToken(refreshToken);
|
|
} catch (error) {
|
|
logger.error('Error verifying refresh token:', error);
|
|
const meetError = errorInvalidRefreshToken();
|
|
return rejectRequestFromMeetError(res, meetError);
|
|
}
|
|
|
|
const username = payload.sub;
|
|
const userService = container.get(UserService);
|
|
const user = username ? await userService.getUser(username) : null;
|
|
|
|
if (!user) {
|
|
logger.warn('Invalid refresh token subject');
|
|
const error = errorInvalidTokenSubject();
|
|
return rejectRequestFromMeetError(res, error);
|
|
}
|
|
|
|
try {
|
|
const accessToken = await tokenService.generateAccessToken(user);
|
|
|
|
logger.info(`Access token refreshed for user '${username}'`);
|
|
const transportMode = await getAuthTransportMode();
|
|
|
|
if (transportMode === AuthTransportMode.HEADER) {
|
|
// Send access token in response body for header mode
|
|
return res.status(200).json({
|
|
message: `Access token for user '${username}' successfully refreshed`,
|
|
accessToken
|
|
});
|
|
} else {
|
|
// Send access token as cookie for cookie mode
|
|
res.cookie(
|
|
INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME,
|
|
accessToken,
|
|
getCookieOptions('/', INTERNAL_CONFIG.ACCESS_TOKEN_EXPIRATION)
|
|
);
|
|
return res.status(200).json({ message: `Access token for user '${username}' successfully refreshed` });
|
|
}
|
|
} catch (error) {
|
|
handleError(res, error, 'refreshing token');
|
|
}
|
|
};
|
|
|
|
export const createApiKey = async (_req: Request, res: Response) => {
|
|
const logger = container.get(LoggerService);
|
|
logger.verbose('Create API key request received');
|
|
|
|
const authService = container.get(AuthService);
|
|
|
|
try {
|
|
const apiKey = await authService.createApiKey();
|
|
return res.status(201).json(apiKey);
|
|
} catch (error) {
|
|
handleError(res, error, 'creating API key');
|
|
}
|
|
};
|
|
|
|
export const getApiKeys = async (_req: Request, res: Response) => {
|
|
const logger = container.get(LoggerService);
|
|
logger.verbose('Get API keys request received');
|
|
|
|
const authService = container.get(AuthService);
|
|
|
|
try {
|
|
const apiKeys = await authService.getApiKeys();
|
|
return res.status(200).json(apiKeys);
|
|
} catch (error) {
|
|
handleError(res, error, 'getting API keys');
|
|
}
|
|
};
|
|
|
|
export const deleteApiKeys = async (_req: Request, res: Response) => {
|
|
const logger = container.get(LoggerService);
|
|
logger.verbose('Delete API keys request received');
|
|
|
|
const authService = container.get(AuthService);
|
|
|
|
try {
|
|
await authService.deleteApiKeys();
|
|
return res.status(200).json({ message: 'API keys deleted successfully' });
|
|
} catch (error) {
|
|
handleError(res, error, 'deleting API keys');
|
|
}
|
|
};
|