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) => { router.post('/signin', strictAuthLimiter, async (req, res) => {
try { 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 обязателен' }); return res.status(400).json({ error: 'initData обязателен' });
} }
let telegramUser; let payload;
try { try {
({ telegramUser } = validateAndParseInitData(initData, req)); payload = validateAndParseInitData(initDataRaw);
} catch (error) { } catch (error) {
logSecurityEvent('INVALID_INITDATA', req, { reason: error.message });
return res.status(401).json({ error: error.message }); return res.status(401).json({ error: error.message });
} }
const telegramUser = payload.user;
if (!validateTelegramId(telegramUser.id)) { if (!validateTelegramId(telegramUser.id)) {
logSecurityEvent('INVALID_TELEGRAM_ID', req, { telegramId: telegramUser.id }); logSecurityEvent('INVALID_TELEGRAM_ID', req, { telegramId: telegramUser.id });
return res.status(400).json({ error: 'Неверный 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 config = require('../config');
const MAX_AUTH_AGE_SECONDS = 5 * 60; const MAX_AUTH_AGE_SECONDS = 5 * 60;
function validateAndParseInitData(initData, req) { function validateAndParseInitData(initDataRaw) {
if (!config.telegramBotToken) { if (!config.telegramBotToken) {
throw new Error('TELEGRAM_BOT_TOKEN не настроен'); throw new Error('TELEGRAM_BOT_TOKEN не настроен');
} }
const params = new URLSearchParams(initData); if (!initDataRaw || typeof initDataRaw !== 'string') {
const hash = params.get('hash'); throw new Error('initData не передан');
const authDate = Number(params.get('auth_date'));
if (!hash) {
throw new Error('Отсутствует hash в 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) { if (!authDate) {
throw new Error('Отсутствует auth_date в initData'); 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); const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - authDate) > MAX_AUTH_AGE_SECONDS) { if (Math.abs(now - authDate) > MAX_AUTH_AGE_SECONDS) {
throw new Error('Данные авторизации устарели'); throw new Error('Данные авторизации устарели');
} }
const userParam = params.get('user'); return payload;
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 };
} }
module.exports = { 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 // Auth API
export const signInWithTelegram = async (initData) => { export const signInWithTelegram = async (initData) => {
const response = await api.post('/auth/signin', { 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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<title>Nakama Moderation</title> <title>Nakama Moderation</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -9,6 +9,17 @@ const api = axios.create({
withCredentials: true 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) => export const signInWithTelegram = (initData) =>
api.post('/auth/signin', { initData }).then((res) => res.data.user) api.post('/auth/signin', { initData }).then((res) => res.data.user)

View File

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