'use strict'; /** * ui/routes/auth.js — /login, /logout для UI-слоя. * * Два режима: * Mock (DEV_MODE=true) — страница с пресетами MOCK_USERS. * Выбранный пользователь → генерируем mock Bearer token → session.token. * * Token (всегда доступен) — форма вставки реального Bearer токена от auth-api/KC. * Вставленный токен → session.token (без проверки подписи здесь, * API сам проверит при первом запросе). * * /logout — уничтожает сессию. */ const { Router } = require('express'); function createRouter({ auth, MOCK_USERS, authLimiter }) { const router = Router(); const devMode = () => process.env.DEV_MODE === 'true'; // GET /login router.get('/login', (req, res) => { if (req.session && req.session.token) return res.redirect('/'); res.render('ui-login', { devMode: devMode(), MOCK_USERS: devMode() ? MOCK_USERS : [], error: req.query.error || null, returnTo: req.query.returnTo || '/', appVersion: require('../../package.json').version, }); }); // POST /login — mock user router.post('/login', authLimiter, (req, res) => { const { clientId, returnTo } = req.body; if (!clientId) return res.redirect('/login?error=Выберите+пользователя'); const user = MOCK_USERS.find(u => u.clientId === clientId); if (!user) return res.redirect('/login?error=Пользователь+не+найден'); // Разбор clientId с запятой (мульти-компания) const allClientIds = user.clientId.split(',').map(s => s.trim()).filter(Boolean); const activeClientId = allClientIds[0] || user.clientId; const token = auth.issueMockToken({ clientId: user.clientId, companyId: user.companyId || '00000000-0000-0000-0000-000000000001', companyName: user.companyName || user.clientId, email: user.email || user.clientId + '@mock.local', }); req.session.token = token; req.session.user = { clientId: user.clientId, allClientIds, activeClientId, email: user.email || user.clientId + '@mock.local', companyId: user.companyId || '00000000-0000-0000-0000-000000000001', companyName: user.companyName || user.clientId, isAdmin: user.isAdmin || user.role === 'admin', }; res.redirect(returnTo || '/'); }); // GET /login-token — редирект на /login (форма вставки токена там же) router.get('/login-token', (req, res) => res.redirect('/login')); // POST /login-token — вход по Bearer JWT токену (реальному) или clientId (отладка) router.post('/login-token', authLimiter, (req, res) => { const { token, clientId, returnTo } = req.body; // ── Вход по реальному JWT токену ────────────────────────────────────── if (token && token.trim()) { const raw = token.trim().replace(/^Bearer\s+/i, ''); // Минимальная проверка: это JWT (3 части, разделённые точками) const parts = raw.split('.'); if (parts.length !== 3) { return res.redirect('/login?error=Невалидный+JWT+токен'); } // Декодируем payload (без проверки подписи — API проверит) let payload; try { const buf = Buffer.from(parts[1], 'base64url'); payload = JSON.parse(buf.toString()); if (!payload || typeof payload !== 'object') throw new Error('empty'); } catch (_) { return res.redirect('/login?error=Невалидный+payload+токена'); } // Извлекаем данные пользователя из payload const rawClientId = payload.ClientID || payload.client_id || payload.preferred_username || payload.sub || ''; const allClientIds = rawClientId.split(',').map(s => s.trim()).filter(Boolean); const activeClientId = allClientIds[0] || rawClientId; req.session.token = raw; // Пытаемся обогатить через IAM API try { const { fetchIamUser } = require('../../src/auth'); const iamData = await fetchIamUser(raw); req.session.user = { email: iamData.email, clientId: iamData.clientId, allClientIds: iamData.allClientIds, activeClientId: iamData.clientId, activeProfileId: iamData.activeProfileId, companyId: iamData.companyId, companyName: iamData.companyName, isAdmin: iamData.isAdmin, fio: iamData.fio, profiles: iamData.profiles, }; console.log('[login-token] IAM enrichment OK:', iamData.email, iamData.clientId); } catch (iamErr) { // IAM недоступен — fallback из JWT console.warn('[login-token] IAM enrichment failed, using JWT fallback:', iamErr.message); req.session.user = { clientId: rawClientId, allClientIds, activeClientId, email: payload.email || payload.login || activeClientId + '@unknown', companyId: payload.company_id || payload.companyId || null, companyName: payload.company_name || payload.companyName || activeClientId, isAdmin: activeClientId === (process.env.ADMIN_CLIENT_ID || 'WZ01112'), }; } return res.redirect(returnTo || '/'); } // ── Вход по clientId (отладка, mock) ─────────────────────────────────── if (!clientId || !clientId.trim()) return res.redirect('/login?error=Введите+clientId'); const trimmed = clientId.trim(); const allClientIds = trimmed.split(',').map(s => s.trim()).filter(Boolean); const activeClientId = allClientIds[0]; const mockToken = auth.issueMockToken({ clientId: trimmed, companyId: '00000000-0000-0000-0000-000000000001', companyName: activeClientId, email: activeClientId + '@debug.local', }); req.session.token = mockToken; req.session.user = { clientId: trimmed, allClientIds, activeClientId, email: activeClientId + '@debug.local', companyId: '00000000-0000-0000-0000-000000000001', companyName: activeClientId, isAdmin: activeClientId === (process.env.ADMIN_CLIENT_ID || 'WZ01112'), }; res.redirect(returnTo || '/'); }); // GET /logout router.get('/logout', (req, res) => { req.session.destroy(() => res.redirect('/login')); }); return router; } module.exports = { createRouter };