backend: Refactor authentication controller to improve login flow and add user profile retrieval
This commit is contained in:
parent
d1af9637a6
commit
fbbef9eedf
@ -6,81 +6,56 @@ import { LoggerService } from '../services/logger.service.js';
|
|||||||
import {
|
import {
|
||||||
ACCESS_TOKEN_COOKIE_NAME,
|
ACCESS_TOKEN_COOKIE_NAME,
|
||||||
MEET_ACCESS_TOKEN_EXPIRATION,
|
MEET_ACCESS_TOKEN_EXPIRATION,
|
||||||
MEET_ADMIN_USER,
|
|
||||||
MEET_API_BASE_PATH_V1,
|
MEET_API_BASE_PATH_V1,
|
||||||
MEET_REFRESH_TOKEN_EXPIRATION,
|
MEET_REFRESH_TOKEN_EXPIRATION,
|
||||||
REFRESH_TOKEN_COOKIE_NAME
|
REFRESH_TOKEN_COOKIE_NAME
|
||||||
} from '../environment.js';
|
} from '../environment.js';
|
||||||
import { ClaimGrants } from 'livekit-server-sdk';
|
import { ClaimGrants } from 'livekit-server-sdk';
|
||||||
import { getCookieOptions } from '../utils/cookie-utils.js';
|
import { getCookieOptions } from '../utils/cookie-utils.js';
|
||||||
|
import { UserService } from '../services/user.service.js';
|
||||||
|
|
||||||
export const login = (req: Request, res: Response) => {
|
export const login = async (req: Request, res: Response) => {
|
||||||
const logger = container.get(LoggerService);
|
const logger = container.get(LoggerService);
|
||||||
logger.verbose('Login request received');
|
logger.verbose('Login request received');
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body as { username: string; password: string };
|
||||||
|
|
||||||
if (!username || !password) {
|
|
||||||
logger.warn('Missing username or password');
|
|
||||||
return res.status(400).json({ message: 'Missing username or password' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const authService = container.get(AuthService);
|
const authService = container.get(AuthService);
|
||||||
const authenticated = authService.authenticateUser(username, password);
|
const user = authService.authenticate(username, password);
|
||||||
|
|
||||||
if (!authenticated) {
|
if (!user) {
|
||||||
logger.warn('Login failed');
|
logger.warn('Login failed');
|
||||||
return res.status(401).json({ message: 'Login failed' });
|
return res.status(404).json({ message: 'Login failed. Invalid username or password' });
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(200).json({ message: 'Login succeeded' });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const logout = (req: Request, res: Response) => {
|
|
||||||
return res.status(200).json({ message: 'Logout successful' });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const adminLogin = async (req: Request, res: Response) => {
|
|
||||||
const logger = container.get(LoggerService);
|
|
||||||
logger.verbose('Admin login request received');
|
|
||||||
const { username, password } = req.body;
|
|
||||||
|
|
||||||
const authService = container.get(AuthService);
|
|
||||||
const authenticated = authService.authenticateAdmin(username, password);
|
|
||||||
|
|
||||||
if (!authenticated) {
|
|
||||||
logger.warn(`Admin login failed for username: ${username}`);
|
|
||||||
return res.status(404).json({ message: 'Admin login failed. Invalid username or password' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tokenService = container.get(TokenService);
|
const tokenService = container.get(TokenService);
|
||||||
const accessToken = await tokenService.generateAccessToken(username);
|
const accessToken = await tokenService.generateAccessToken(user);
|
||||||
const refreshToken = await tokenService.generateRefreshToken(username);
|
const refreshToken = await tokenService.generateRefreshToken(user);
|
||||||
res.cookie(ACCESS_TOKEN_COOKIE_NAME, accessToken, getCookieOptions('/', MEET_ACCESS_TOKEN_EXPIRATION));
|
res.cookie(ACCESS_TOKEN_COOKIE_NAME, accessToken, getCookieOptions('/', MEET_ACCESS_TOKEN_EXPIRATION));
|
||||||
res.cookie(
|
res.cookie(
|
||||||
REFRESH_TOKEN_COOKIE_NAME,
|
REFRESH_TOKEN_COOKIE_NAME,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
getCookieOptions(`${MEET_API_BASE_PATH_V1}/auth/admin`, MEET_REFRESH_TOKEN_EXPIRATION)
|
getCookieOptions(`${MEET_API_BASE_PATH_V1}/auth`, MEET_REFRESH_TOKEN_EXPIRATION)
|
||||||
);
|
);
|
||||||
logger.info(`Admin login succeeded for username: ${username}`);
|
logger.info(`Login succeeded for user ${username}`);
|
||||||
return res.status(200).json({ message: 'Admin login succeeded' });
|
return res.status(200).json({ message: 'Login succeeded' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error generating admin token' + error);
|
logger.error('Error generating token' + error);
|
||||||
return res.status(500).json({ message: 'Internal server error' });
|
return res.status(500).json({ message: 'Internal server error' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const adminLogout = (req: Request, res: Response) => {
|
export const logout = (_req: Request, res: Response) => {
|
||||||
res.clearCookie(ACCESS_TOKEN_COOKIE_NAME);
|
res.clearCookie(ACCESS_TOKEN_COOKIE_NAME);
|
||||||
res.clearCookie(REFRESH_TOKEN_COOKIE_NAME, {
|
res.clearCookie(REFRESH_TOKEN_COOKIE_NAME, {
|
||||||
path: `${MEET_API_BASE_PATH_V1}/auth/admin`
|
path: `${MEET_API_BASE_PATH_V1}/auth`
|
||||||
});
|
});
|
||||||
return res.status(200).json({ message: 'Logout successful' });
|
return res.status(200).json({ message: 'Logout successful' });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const adminRefresh = async (req: Request, res: Response) => {
|
export const refreshToken = async (req: Request, res: Response) => {
|
||||||
const logger = container.get(LoggerService);
|
const logger = container.get(LoggerService);
|
||||||
logger.verbose('Admin refresh request received');
|
logger.verbose('Refresh token request received');
|
||||||
const refreshToken = req.cookies[REFRESH_TOKEN_COOKIE_NAME];
|
const refreshToken = req.cookies[REFRESH_TOKEN_COOKIE_NAME];
|
||||||
|
|
||||||
if (!refreshToken) {
|
if (!refreshToken) {
|
||||||
@ -98,18 +73,32 @@ export const adminRefresh = async (req: Request, res: Response) => {
|
|||||||
return res.status(400).json({ message: 'Invalid refresh token' });
|
return res.status(400).json({ message: 'Invalid refresh token' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.sub !== MEET_ADMIN_USER) {
|
const username = payload.sub;
|
||||||
|
const userService = container.get(UserService);
|
||||||
|
const user = username ? userService.getUser(username) : null;
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
logger.warn('Invalid refresh token subject');
|
logger.warn('Invalid refresh token subject');
|
||||||
return res.status(403).json({ message: 'Invalid refresh token subject' });
|
return res.status(403).json({ message: 'Invalid refresh token subject' });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const accessToken = await tokenService.generateAccessToken(MEET_ADMIN_USER);
|
const accessToken = await tokenService.generateAccessToken(user);
|
||||||
res.cookie(ACCESS_TOKEN_COOKIE_NAME, accessToken, getCookieOptions('/', MEET_ACCESS_TOKEN_EXPIRATION));
|
res.cookie(ACCESS_TOKEN_COOKIE_NAME, accessToken, getCookieOptions('/', MEET_ACCESS_TOKEN_EXPIRATION));
|
||||||
logger.info(`Admin refresh succeeded for username: ${MEET_ADMIN_USER}`);
|
logger.info(`Token refreshed for user ${username}`);
|
||||||
return res.status(200).json({ message: 'Admin refresh succeeded' });
|
return res.status(200).json({ message: 'Token refreshed' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error refreshing admin token' + error);
|
logger.error('Error refreshing token' + error);
|
||||||
return res.status(500).json({ message: 'Internal server error' });
|
return res.status(500).json({ message: 'Internal server error' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getProfile = (req: Request, res: Response) => {
|
||||||
|
const user = req.session?.user;
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(401).json({ message: 'Unauthorized' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).json(user);
|
||||||
|
};
|
||||||
|
|||||||
@ -1,27 +1,29 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
import { Router } from 'express';
|
||||||
import bodyParser from 'body-parser';
|
import bodyParser from 'body-parser';
|
||||||
import * as authCtrl from '../controllers/auth.controller.js';
|
import * as authCtrl from '../controllers/auth.controller.js';
|
||||||
import rateLimit from 'express-rate-limit';
|
import rateLimit from 'express-rate-limit';
|
||||||
import { withAdminValidToken } from '../middlewares/auth.middleware.js';
|
import { tokenAndRoleValidator, withAuth } from '../middlewares/auth.middleware.js';
|
||||||
|
import { Role } from '@typings-ce';
|
||||||
|
import { validateLoginRequest } from '../middlewares/request-validators/auth-validator.middleware.js';
|
||||||
|
|
||||||
export const authRouter = Router();
|
export const authRouter = Router();
|
||||||
|
|
||||||
// Limit login attempts for avoiding brute force attacks
|
// Limit login attempts for avoiding brute force attacks
|
||||||
const loginLimiter = rateLimit({
|
const loginLimiter = rateLimit({
|
||||||
windowMs: 15 * 60 * 1000, // 15 min
|
windowMs: 15 * 60 * 1000, // 15 min
|
||||||
max: 5,
|
limit: 5,
|
||||||
message: 'Too many login attempts, please try again later.'
|
message: 'Too many login attempts, please try again later'
|
||||||
});
|
});
|
||||||
|
|
||||||
authRouter.use(bodyParser.urlencoded({ extended: true }));
|
authRouter.use(bodyParser.urlencoded({ extended: true }));
|
||||||
authRouter.use(bodyParser.json());
|
authRouter.use(bodyParser.json());
|
||||||
|
|
||||||
// Auth Routes
|
// Auth Routes
|
||||||
authRouter.post('/login', authCtrl.login);
|
authRouter.post('/login', validateLoginRequest, loginLimiter, authCtrl.login);
|
||||||
authRouter.post('/logout', authCtrl.logout);
|
authRouter.post('/logout', authCtrl.logout);
|
||||||
authRouter.post('/admin/login', loginLimiter, authCtrl.adminLogin);
|
authRouter.post('/refresh', authCtrl.refreshToken);
|
||||||
authRouter.post('/admin/logout', authCtrl.adminLogout);
|
authRouter.get(
|
||||||
authRouter.post('/admin/refresh', authCtrl.adminRefresh);
|
'/profile',
|
||||||
authRouter.get('/admin/verify', withAdminValidToken, (_req: Request, res: Response) =>
|
withAuth(tokenAndRoleValidator(Role.ADMIN), tokenAndRoleValidator(Role.USER)),
|
||||||
res.status(200).json({ message: 'Valid token' })
|
authCtrl.getProfile
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user