Update files

This commit is contained in:
glpshchn 2025-11-11 01:19:37 +03:00
parent d6fcfc5c17
commit d2361b0e10
6 changed files with 62 additions and 47 deletions

View File

@ -36,20 +36,27 @@ const OFFICIAL_CLIENT_MESSAGE = 'Используйте официальный
router.post('/signin', strictAuthLimiter, async (req, res) => {
try {
const { initData } = req.body || {};
const authHeader = req.headers.authorization || '';
const headerInitData = authHeader.startsWith('tma ') ? authHeader.slice(4).trim() : null;
const bodyInitData = typeof req.body?.initData === 'string' ? req.body.initData : null;
if (!initData || typeof initData !== 'string') {
const initDataRaw = headerInitData || bodyInitData;
if (!initDataRaw) {
return res.status(400).json({ error: 'initData обязателен' });
}
let telegramUser;
let payload;
try {
({ telegramUser } = validateAndParseInitData(initData, req));
payload = validateAndParseInitData(initDataRaw);
} catch (error) {
logSecurityEvent('INVALID_INITDATA', req, { reason: error.message });
return res.status(401).json({ error: error.message });
}
const telegramUser = payload.user;
if (!validateTelegramId(telegramUser.id)) {
logSecurityEvent('INVALID_TELEGRAM_ID', req, { telegramId: telegramUser.id });
return res.status(400).json({ error: 'Неверный ID пользователя' });

View File

@ -1,64 +1,48 @@
const crypto = require('crypto');
const { parse, isValid } = require('@telegram-apps/init-data-node');
const config = require('../config');
const MAX_AUTH_AGE_SECONDS = 5 * 60;
function validateAndParseInitData(initData, req) {
function validateAndParseInitData(initDataRaw) {
if (!config.telegramBotToken) {
throw new Error('TELEGRAM_BOT_TOKEN не настроен');
}
const params = new URLSearchParams(initData);
const hash = params.get('hash');
const authDate = Number(params.get('auth_date'));
if (!hash) {
throw new Error('Отсутствует hash в initData');
if (!initDataRaw || typeof initDataRaw !== 'string') {
throw new Error('initData не передан');
}
const trimmed = initDataRaw.trim();
if (!trimmed.length) {
throw new Error('initData пуст');
}
const valid = isValid(trimmed, config.telegramBotToken);
if (!valid) {
throw new Error('Неверная подпись initData');
}
const payload = parse(trimmed);
if (!payload || !payload.user) {
throw new Error('Отсутствует пользователь в initData');
}
const authDate = Number(payload.auth_date);
if (!authDate) {
throw new Error('Отсутствует auth_date в initData');
}
const dataCheck = [];
for (const [key, value] of params.entries()) {
if (key === 'hash') continue;
dataCheck.push(`${key}=${value}`);
}
dataCheck.sort((a, b) => a.localeCompare(b));
const dataCheckString = dataCheck.join('\n');
const secretKey = crypto.createHmac('sha256', 'WebAppData').update(config.telegramBotToken).digest();
const calculatedHash = crypto.createHmac('sha256', secretKey).update(dataCheckString).digest('hex');
if (calculatedHash !== hash) {
throw new Error('Неверная подпись initData');
}
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - authDate) > MAX_AUTH_AGE_SECONDS) {
throw new Error('Данные авторизации устарели');
}
const userParam = params.get('user');
if (!userParam) {
throw new Error('Отсутствует пользователь в initData');
}
let telegramUser;
try {
telegramUser = JSON.parse(userParam);
} catch (error) {
throw new Error('Некорректный формат user в initData');
}
if (!telegramUser || !telegramUser.id) {
throw new Error('Отсутствует ID пользователя в initData');
}
return { params, telegramUser };
return payload;
}
module.exports = {

View File

@ -16,6 +16,17 @@ const api = axios.create({
}
})
api.interceptors.request.use((config) => {
const initData = window.Telegram?.WebApp?.initData;
if (initData) {
config.headers = config.headers || {};
if (!config.headers.Authorization) {
config.headers.Authorization = `tma ${initData}`;
}
}
return config;
});
// Auth API
export const signInWithTelegram = async (initData) => {
const response = await api.post('/auth/signin', { initData })

View File

@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#000000" />
<title>Nakama Moderation</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
</head>
<body>
<div id="root"></div>

View File

@ -9,6 +9,17 @@ const api = axios.create({
withCredentials: true
})
api.interceptors.request.use((config) => {
const initData = window.Telegram?.WebApp?.initData;
if (initData) {
config.headers = config.headers || {};
if (!config.headers.Authorization) {
config.headers.Authorization = `tma ${initData}`;
}
}
return config;
});
export const signInWithTelegram = (initData) =>
api.post('/auth/signin', { initData }).then((res) => res.data.user)

View File

@ -36,7 +36,8 @@
"xss-clean": "^0.1.4",
"hpp": "^0.2.3",
"validator": "^13.11.0",
"cookie-parser": "^1.4.6"
"cookie-parser": "^1.4.6",
"@telegram-apps/init-data-node": "^1.0.0"
},
"devDependencies": {
"nodemon": "^3.0.1",