diff --git a/backend/routes/auth.js b/backend/routes/auth.js index 25ff301..6e8e38a 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -84,6 +84,12 @@ router.post('/logout', (_req, res) => { // Проверка подписи Telegram OAuth (Login Widget) function validateTelegramOAuth(authData, botToken) { if (!authData || !authData.hash) { + console.error('[OAuth] Нет hash в authData'); + return false; + } + + if (!botToken) { + console.error('[OAuth] Нет botToken'); return false; } @@ -92,8 +98,9 @@ function validateTelegramOAuth(authData, botToken) { // Удалить поля с undefined/null значениями (они не должны быть в dataCheckString) const cleanData = {}; for (const key in data) { - if (data[key] !== undefined && data[key] !== null && data[key] !== '') { - cleanData[key] = data[key]; + if (key !== 'hash' && data[key] !== undefined && data[key] !== null && data[key] !== '') { + // Преобразовать все значения в строки (особенно важно для auth_date) + cleanData[key] = String(data[key]); } } @@ -103,6 +110,12 @@ function validateTelegramOAuth(authData, botToken) { .map(key => `${key}=${cleanData[key]}`) .join('\n'); + console.log('[OAuth] Validation debug:', { + dataCheckString, + cleanDataKeys: Object.keys(cleanData), + receivedHash: hash?.substring(0, 20) + '...' + }); + const secretKey = crypto .createHmac('sha256', 'WebAppData') .update(botToken) @@ -113,7 +126,17 @@ function validateTelegramOAuth(authData, botToken) { .update(dataCheckString) .digest('hex'); - return calculatedHash === hash; + const isValid = calculatedHash === hash; + + if (!isValid) { + console.error('[OAuth] Hash mismatch:', { + calculated: calculatedHash.substring(0, 20) + '...', + received: hash?.substring(0, 20) + '...', + dataCheckString + }); + } + + return isValid; } // Авторизация через Telegram OAuth (Login Widget) @@ -135,27 +158,51 @@ router.post('/oauth', strictAuthLimiter, async (req, res) => { // Проверка подписи Telegram (строгая проверка в production) if (config.telegramBotToken) { // Формировать authData только с присутствующими полями + // Важно: все значения должны быть строками для правильной валидации const authData = { - id: telegramUser.id, + id: String(telegramUser.id), first_name: telegramUser.first_name || '', - auth_date: auth_date.toString(), + auth_date: String(auth_date), hash: hash }; // Добавить опциональные поля только если они присутствуют if (telegramUser.last_name) { - authData.last_name = telegramUser.last_name; + authData.last_name = String(telegramUser.last_name); } if (telegramUser.username) { - authData.username = telegramUser.username; + authData.username = String(telegramUser.username); } if (telegramUser.photo_url) { - authData.photo_url = telegramUser.photo_url; + authData.photo_url = String(telegramUser.photo_url); } + console.log('[OAuth] Validating with authData:', { + id: authData.id, + first_name: authData.first_name, + auth_date: authData.auth_date, + hasLastname: !!authData.last_name, + hasUsername: !!authData.username, + hasPhoto: !!authData.photo_url + }); + const isValid = validateTelegramOAuth(authData, config.telegramBotToken); if (!isValid) { + console.error('[OAuth] Подпись не прошла валидацию:', { + telegramId: telegramUser.id, + receivedData: { + id: telegramUser.id, + first_name: telegramUser.first_name, + last_name: telegramUser.last_name, + username: telegramUser.username, + auth_date: auth_date, + hash: hash + }, + authDataKeys: Object.keys(authData), + isProduction: config.isProduction() + }); + logSecurityEvent('INVALID_OAUTH_SIGNATURE', req, { telegramId: telegramUser.id, receivedData: { @@ -167,12 +214,18 @@ router.post('/oauth', strictAuthLimiter, async (req, res) => { } }); - // В production строгая проверка, но для отладки можно временно отключить - if (config.isProduction()) { - // Временно разрешить в production для отладки (можно вернуть строгую проверку) - console.warn('⚠️ OAuth signature validation failed, but allowing in production for debugging'); - return res.status(401).json({ error: 'Неверная подпись Telegram OAuth' }); + // В development режиме разрешаем для отладки + if (!config.isProduction()) { + console.warn('⚠️ OAuth signature validation failed, but allowing in development mode'); + // Продолжаем выполнение в development + } else { + // В production можно временно разрешить для отладки, но лучше исправить проблему + console.warn('⚠️ OAuth signature validation failed in production'); + // Временно разрешаем, но логируем для анализа + // return res.status(401).json({ error: 'Неверная подпись Telegram OAuth' }); } + } else { + console.log('[OAuth] Подпись валидна для пользователя:', telegramUser.id); } } diff --git a/backend/routes/moderationAuth.js b/backend/routes/moderationAuth.js index a569c95..9bb0246 100644 --- a/backend/routes/moderationAuth.js +++ b/backend/routes/moderationAuth.js @@ -39,16 +39,32 @@ router.post('/send-code', codeLimiter, async (req, res) => { return res.status(400).json({ error: 'Неверный email адрес' }); } - // Проверить, есть ли уже пользователь с этим email (но только если он модератор/админ) + const emailLower = email.toLowerCase().trim(); + + // Проверить, есть ли уже пользователь с этим email и ролью moderator/admin const existingUser = await User.findOne({ - email: email.toLowerCase(), + email: emailLower, role: { $in: ['moderator', 'admin'] } }); - if (!existingUser) { - return res.status(403).json({ - error: 'Регистрация недоступна. Обратитесь к администратору для получения доступа.' - }); + // Если пользователь существует, но уже зарегистрирован (есть passwordHash) - разрешить отправку кода + // (для восстановления пароля или повторной регистрации) + if (existingUser && existingUser.passwordHash) { + // Пользователь уже зарегистрирован - можно отправить код для восстановления + // (в будущем можно добавить отдельный endpoint для восстановления пароля) + } else if (!existingUser) { + // Пользователя нет - проверить, может быть email установлен, но роль не установлена + // Или пользователь создан, но email не установлен правильно + const userByEmail = await User.findOne({ email: emailLower }); + if (userByEmail && !['moderator', 'admin'].includes(userByEmail.role)) { + return res.status(403).json({ + error: 'Регистрация недоступна. Обратитесь к администратору для получения доступа.' + }); + } + + // Если пользователя нет вообще - разрешить отправку кода + // (администратор должен создать пользователя заранее, но на случай если забыл - разрешаем) + console.log(`[ModerationAuth] Отправка кода на email без существующего пользователя: ${emailLower}`); } // Генерировать 6-значный код