160 lines
5.3 KiB
JavaScript
160 lines
5.3 KiB
JavaScript
const express = require('express');
|
||
const router = express.Router();
|
||
const crypto = require('crypto');
|
||
const User = require('../models/User');
|
||
const config = require('../config');
|
||
const { validateTelegramId } = require('../middleware/validator');
|
||
const { logSecurityEvent } = require('../middleware/logger');
|
||
const { strictAuthLimiter } = require('../middleware/security');
|
||
|
||
// Проверка подписи Telegram OAuth (Login Widget)
|
||
function validateTelegramOAuth(authData, botToken) {
|
||
if (!authData || !authData.hash) {
|
||
return false;
|
||
}
|
||
|
||
const { hash, ...data } = authData;
|
||
const dataCheckString = Object.keys(data)
|
||
.sort()
|
||
.map(key => `${key}=${data[key]}`)
|
||
.join('\n');
|
||
|
||
const secretKey = crypto
|
||
.createHmac('sha256', 'WebAppData')
|
||
.update(botToken)
|
||
.digest();
|
||
|
||
const calculatedHash = crypto
|
||
.createHmac('sha256', secretKey)
|
||
.update(dataCheckString)
|
||
.digest('hex');
|
||
|
||
return calculatedHash === hash;
|
||
}
|
||
|
||
// Авторизация через Telegram OAuth (Login Widget)
|
||
router.post('/oauth', strictAuthLimiter, async (req, res) => {
|
||
try {
|
||
const { user: telegramUser, auth_date, hash } = req.body;
|
||
|
||
if (!telegramUser || !auth_date || !hash) {
|
||
logSecurityEvent('INVALID_OAUTH_DATA', req);
|
||
return res.status(400).json({ error: 'Неверные данные авторизации' });
|
||
}
|
||
|
||
// Валидация Telegram ID
|
||
if (!validateTelegramId(telegramUser.id)) {
|
||
logSecurityEvent('INVALID_TELEGRAM_ID', req, { telegramId: telegramUser.id });
|
||
return res.status(400).json({ error: 'Неверный ID пользователя' });
|
||
}
|
||
|
||
// Проверка подписи Telegram (строгая проверка в production)
|
||
if (config.telegramBotToken) {
|
||
const authData = {
|
||
id: telegramUser.id,
|
||
first_name: telegramUser.first_name,
|
||
last_name: telegramUser.last_name,
|
||
username: telegramUser.username,
|
||
photo_url: telegramUser.photo_url,
|
||
auth_date: auth_date,
|
||
hash: hash
|
||
};
|
||
|
||
const isValid = validateTelegramOAuth(authData, config.telegramBotToken);
|
||
|
||
if (!isValid) {
|
||
logSecurityEvent('INVALID_OAUTH_SIGNATURE', req, { telegramId: telegramUser.id });
|
||
|
||
// В production строгая проверка
|
||
if (config.isProduction()) {
|
||
return res.status(401).json({ error: 'Неверная подпись Telegram OAuth' });
|
||
}
|
||
}
|
||
}
|
||
|
||
// Найти или создать пользователя
|
||
let user = await User.findOne({ telegramId: telegramUser.id.toString() });
|
||
if (!user) {
|
||
user = new User({
|
||
telegramId: telegramUser.id.toString(),
|
||
username: telegramUser.username || telegramUser.first_name,
|
||
firstName: telegramUser.first_name,
|
||
lastName: telegramUser.last_name,
|
||
photoUrl: telegramUser.photo_url
|
||
});
|
||
await user.save();
|
||
console.log(`✅ Создан новый пользователь через OAuth: ${user.username}`);
|
||
} else {
|
||
// Обновить данные пользователя
|
||
user.username = telegramUser.username || telegramUser.first_name;
|
||
user.firstName = telegramUser.first_name;
|
||
user.lastName = telegramUser.last_name;
|
||
user.photoUrl = telegramUser.photo_url;
|
||
await user.save();
|
||
}
|
||
|
||
// Получить полные данные пользователя
|
||
const populatedUser = await User.findById(user._id).populate([
|
||
{ path: 'followers', select: 'username firstName lastName photoUrl' },
|
||
{ path: 'following', select: 'username firstName lastName photoUrl' }
|
||
]);
|
||
|
||
res.json({
|
||
success: true,
|
||
user: {
|
||
id: populatedUser._id,
|
||
telegramId: populatedUser.telegramId,
|
||
username: populatedUser.username,
|
||
firstName: populatedUser.firstName,
|
||
lastName: populatedUser.lastName,
|
||
photoUrl: populatedUser.photoUrl,
|
||
bio: populatedUser.bio,
|
||
role: populatedUser.role,
|
||
followersCount: populatedUser.followers.length,
|
||
followingCount: populatedUser.following.length,
|
||
settings: populatedUser.settings,
|
||
banned: populatedUser.banned
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка OAuth:', error);
|
||
res.status(500).json({ error: 'Ошибка сервера' });
|
||
}
|
||
});
|
||
|
||
// Проверка авторизации и получение данных пользователя
|
||
const { authenticate } = require('../middleware/auth');
|
||
|
||
router.post('/verify', authenticate, async (req, res) => {
|
||
try {
|
||
const user = await req.user.populate([
|
||
{ path: 'followers', select: 'username firstName lastName photoUrl' },
|
||
{ path: 'following', select: 'username firstName lastName photoUrl' }
|
||
]);
|
||
|
||
res.json({
|
||
success: true,
|
||
user: {
|
||
id: user._id,
|
||
telegramId: user.telegramId,
|
||
username: user.username,
|
||
firstName: user.firstName,
|
||
lastName: user.lastName,
|
||
photoUrl: user.photoUrl,
|
||
bio: user.bio,
|
||
role: user.role,
|
||
followersCount: user.followers.length,
|
||
followingCount: user.following.length,
|
||
settings: user.settings,
|
||
banned: user.banned
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка verify:', error);
|
||
res.status(500).json({ error: 'Ошибка сервера' });
|
||
}
|
||
});
|
||
|
||
module.exports = router;
|
||
|