nakama/backend/middleware/auth.js

155 lines
4.6 KiB
JavaScript
Raw Normal View History

2025-11-03 20:35:01 +00:00
const User = require('../models/User');
2025-11-04 21:51:05 +00:00
const { validateTelegramId } = require('./validator');
const { logSecurityEvent } = require('./logger');
const config = require('../config');
2025-11-10 21:56:36 +00:00
const {
ACCESS_COOKIE,
REFRESH_COOKIE,
signAuthTokens,
setAuthCookies,
clearAuthCookies,
verifyAccessToken,
verifyRefreshToken
} = require('../utils/tokens');
2025-11-03 20:35:01 +00:00
2025-11-10 20:13:22 +00:00
const OFFICIAL_CLIENT_MESSAGE = 'Используйте официальный клиент. Сообщите об ошибке в https://t.me/NakamaReportbot';
const ALLOWED_SEARCH_PREFERENCES = ['furry', 'anime'];
const touchUserActivity = async (user) => {
if (!user) return;
const now = Date.now();
const shouldUpdate =
!user.lastActiveAt ||
Math.abs(now - new Date(user.lastActiveAt).getTime()) > 5 * 60 * 1000;
if (shouldUpdate) {
user.lastActiveAt = new Date(now);
await user.save();
}
};
const ensureUserSettings = async (user) => {
if (!user) return;
let updated = false;
if (!user.settings) {
user.settings = {};
updated = true;
}
if (!ALLOWED_SEARCH_PREFERENCES.includes(user.settings.searchPreference)) {
user.settings.searchPreference = 'furry';
updated = true;
}
if (!user.settings.whitelist) {
user.settings.whitelist = { noNSFW: true };
updated = true;
} else if (user.settings.whitelist.noNSFW === undefined) {
user.settings.whitelist.noNSFW = true;
updated = true;
}
if (updated) {
user.markModified('settings');
await user.save();
}
};
2025-11-03 20:35:01 +00:00
// Middleware для проверки авторизации
const authenticate = async (req, res, next) => {
try {
2025-11-10 21:56:36 +00:00
const accessToken = req.cookies[ACCESS_COOKIE];
const refreshToken = req.cookies[REFRESH_COOKIE];
let tokenPayload = null;
if (accessToken) {
try {
tokenPayload = verifyAccessToken(accessToken);
} catch (error) {
if (error.name !== 'TokenExpiredError') {
logSecurityEvent('INVALID_ACCESS_TOKEN', req, { error: error.message });
clearAuthCookies(res);
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
}
2025-11-10 21:22:58 +00:00
}
}
2025-11-10 21:56:36 +00:00
if (!tokenPayload && refreshToken) {
try {
const refreshPayload = verifyRefreshToken(refreshToken);
const userForRefresh = await User.findById(refreshPayload.userId);
if (!userForRefresh) {
clearAuthCookies(res);
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
}
const tokens = signAuthTokens(userForRefresh);
setAuthCookies(res, tokens);
tokenPayload = verifyAccessToken(tokens.accessToken);
} catch (error) {
logSecurityEvent('INVALID_REFRESH_TOKEN', req, { error: error.message });
clearAuthCookies(res);
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
2025-11-03 22:41:34 +00:00
}
2025-11-03 20:35:01 +00:00
}
2025-11-10 21:22:58 +00:00
2025-11-10 21:56:36 +00:00
if (!tokenPayload) {
logSecurityEvent('AUTH_TOKEN_MISSING', req);
2025-11-10 20:13:22 +00:00
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
2025-11-03 22:41:34 +00:00
}
2025-11-10 21:22:58 +00:00
2025-11-10 21:56:36 +00:00
if (!validateTelegramId(tokenPayload.telegramId)) {
logSecurityEvent('INVALID_TELEGRAM_ID', req, { telegramId: tokenPayload.telegramId });
clearAuthCookies(res);
2025-11-04 21:51:05 +00:00
return res.status(401).json({ error: 'Неверный ID пользователя' });
}
2025-11-10 21:56:36 +00:00
let user = await User.findOne({ telegramId: tokenPayload.telegramId.toString() });
2025-11-03 20:35:01 +00:00
if (!user) {
2025-11-10 21:56:36 +00:00
clearAuthCookies(res);
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
2025-11-03 20:35:01 +00:00
}
2025-11-10 21:56:36 +00:00
if (user.banned) {
clearAuthCookies(res);
return res.status(403).json({ error: 'Пользователь заблокирован' });
}
2025-11-10 20:13:22 +00:00
await ensureUserSettings(user);
await touchUserActivity(user);
2025-11-03 20:35:01 +00:00
req.user = user;
2025-11-10 21:56:36 +00:00
req.telegramUser = { id: user.telegramId };
2025-11-03 20:35:01 +00:00
next();
} catch (error) {
2025-11-03 21:29:00 +00:00
console.error('❌ Ошибка авторизации:', error);
2025-11-10 21:22:58 +00:00
res.status(401).json({ error: `Ошибка авторизации. ${OFFICIAL_CLIENT_MESSAGE}` });
2025-11-03 20:35:01 +00:00
}
};
// Middleware для проверки роли модератора
const requireModerator = (req, res, next) => {
if (req.user.role !== 'moderator' && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Требуются права модератора' });
}
next();
};
// Middleware для проверки роли админа
const requireAdmin = (req, res, next) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Требуются права администратора' });
}
next();
};
module.exports = {
authenticate,
requireModerator,
2025-11-10 21:56:36 +00:00
requireAdmin,
touchUserActivity,
ensureUserSettings
2025-11-03 20:35:01 +00:00
};