From c9375fb26e80e546909aef5db25ef06f2446f191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Fri, 29 May 2026 10:45:18 -0300 Subject: [PATCH] Extract backend auth middleware --- Dockerfile | 1 + backend/index.js | 55 +------------------------------------ backend/middleware/auth.js | 56 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 54 deletions(-) create mode 100644 backend/middleware/auth.js diff --git a/Dockerfile b/Dockerfile index 5ecf721..1a94c07 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,6 +27,7 @@ RUN npm ci --omit=dev COPY backend/index.js ./index.js COPY backend/db.js ./db.js COPY backend/config ./config +COPY backend/middleware ./middleware COPY backend/policies ./policies COPY backend/services ./services COPY backend/utils ./utils diff --git a/backend/index.js b/backend/index.js index 65aaf30..b8dd99c 100644 --- a/backend/index.js +++ b/backend/index.js @@ -12,6 +12,7 @@ const { hashSecret, maskSecret } = require('./utils/security'); const transporter = require('./services/mailer'); const { createCorsMiddleware } = require('./config/cors'); const { allowedOrigins, getBaseUrl, getStartupBaseUrl, isProduction, jwtSecret: JWT_SECRET, port: PORT } = require('./config/runtime'); +const { authenticateToken, requireRole } = require('./middleware/auth'); const { canReadUser, canUpdateUser, @@ -71,60 +72,6 @@ app.use('/uploads', express.static(uploadDir, { // --- API Router --- const apiRouter = express.Router(); -// Middleware de autenticação -const authenticateToken = async (req, res, next) => { - // Ignorar rotas de auth - if (req.path.startsWith('/auth/')) return next(); - - const authHeader = req.headers['authorization']; - - // API Key Authentication for n8n/External Integrations - if (authHeader && authHeader.startsWith('Bearer fasto_sk_')) { - const apiKey = authHeader.split(' ')[1]; - try { - const [keys] = await pool.query( - 'SELECT * FROM api_keys WHERE secret_hash = ? OR secret_key = ?', - [hashSecret(apiKey), apiKey] - ); - if (keys.length === 0) return res.status(401).json({ error: 'Chave de API inválida.' }); - - // Update last used timestamp - await pool.query('UPDATE api_keys SET last_used_at = CURRENT_TIMESTAMP WHERE id = ?', [keys[0].id]); - - // Attach a "system/bot" user identity to the request based on the tenant - req.user = { - id: 'bot_integration', - tenant_id: keys[0].tenant_id, - role: 'admin', // Give integration admin privileges within its tenant - is_api_key: true - }; - return next(); - } catch (error) { - console.error('API Key validation error:', error); - return res.status(500).json({ error: 'Erro ao validar chave de API.' }); - } - } - - // Standard JWT Authentication - const token = authHeader && authHeader.split(' ')[1]; - - if (!token) return res.status(401).json({ error: 'Token não fornecido.' }); - - try { - const user = jwt.verify(token, JWT_SECRET); - req.user = user; - next(); - } catch (err) { - return res.status(401).json({ error: 'Token inválido ou expirado.' }); - } -}; - -const requireRole = (roles) => (req, res, next) => { - if (!req.user || !req.user.role) return res.status(401).json({ error: 'Não autenticado.' }); - if (!roles.includes(req.user.role)) return res.status(403).json({ error: 'Acesso negado. Você não tem permissão para realizar esta ação.' }); - next(); -}; - apiRouter.use(authenticateToken); // --- Auth Routes --- diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js new file mode 100644 index 0000000..a9c1db8 --- /dev/null +++ b/backend/middleware/auth.js @@ -0,0 +1,56 @@ +const jwt = require('jsonwebtoken'); +const pool = require('../db'); +const { jwtSecret } = require('../config/runtime'); +const { hashSecret } = require('../utils/security'); + +const authenticateToken = async (req, res, next) => { + if (req.path.startsWith('/auth/')) return next(); + + const authHeader = req.headers['authorization']; + + if (authHeader && authHeader.startsWith('Bearer fasto_sk_')) { + const apiKey = authHeader.split(' ')[1]; + try { + const [keys] = await pool.query( + 'SELECT * FROM api_keys WHERE secret_hash = ? OR secret_key = ?', + [hashSecret(apiKey), apiKey] + ); + if (keys.length === 0) return res.status(401).json({ error: 'Chave de API inválida.' }); + + await pool.query('UPDATE api_keys SET last_used_at = CURRENT_TIMESTAMP WHERE id = ?', [keys[0].id]); + + req.user = { + id: 'bot_integration', + tenant_id: keys[0].tenant_id, + role: 'admin', + is_api_key: true + }; + return next(); + } catch (error) { + console.error('API Key validation error:', error); + return res.status(500).json({ error: 'Erro ao validar chave de API.' }); + } + } + + const token = authHeader && authHeader.split(' ')[1]; + + if (!token) return res.status(401).json({ error: 'Token não fornecido.' }); + + try { + req.user = jwt.verify(token, jwtSecret); + next(); + } catch (err) { + return res.status(401).json({ error: 'Token inválido ou expirado.' }); + } +}; + +const requireRole = (roles) => (req, res, next) => { + if (!req.user || !req.user.role) return res.status(401).json({ error: 'Não autenticado.' }); + if (!roles.includes(req.user.role)) return res.status(403).json({ error: 'Acesso negado. Você não tem permissão para realizar esta ação.' }); + next(); +}; + +module.exports = { + authenticateToken, + requireRole, +};