diff --git a/server/middleware/auth.js b/server/middleware/auth.js index 2b7d0e3..bfc1d70 100644 --- a/server/middleware/auth.js +++ b/server/middleware/auth.js @@ -15,7 +15,7 @@ async function authenticateToken(req, res, next) { try { const decoded = jwt.verify(token, process.env.JWT_SECRET); const user = await User.findByPk(decoded.userId, { - attributes: ['id', 'username', 'email', 'role', 'is_active'] + attributes: ['id', 'username', 'email', 'role', 'is_active', 'tenant_id'] }); if (!user || !user.is_active) { @@ -26,6 +26,12 @@ async function authenticateToken(req, res, next) { } req.user = user; + + // Extract tenant info from JWT token if available + if (decoded.tenantId) { + req.tenantId = decoded.tenantId; + } + next(); } catch (error) { console.error('Token verification error:', error); diff --git a/server/routes/auth.js b/server/routes/auth.js index 783e7ad..7b66757 100644 --- a/server/routes/auth.js +++ b/server/routes/auth.js @@ -215,10 +215,70 @@ router.post('/local', async (req, res, next) => { }); } - req.tenant = { id: tenantId, authConfig }; + // Perform local authentication with tenant context + const bcrypt = require('bcryptjs'); + const { User, Tenant } = require('../models'); + const { Op } = require('sequelize'); - // Call tenant-aware local login - return require('../routes/user').loginLocal(req, res, next); + const { username, password } = req.body; + + if (!username || !password) { + return res.status(400).json({ + success: false, + message: 'Username and password are required' + }); + } + + // Find tenant + const tenant = await Tenant.findOne({ where: { slug: tenantId } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' + }); + } + + // Find user by username or email within this tenant + const user = await User.findOne({ + where: { + [Op.or]: [ + { username: username }, + { email: username } + ], + is_active: true, + tenant_id: tenant.id + } + }); + + if (!user || !await bcrypt.compare(password, user.password_hash)) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Update last login + await user.update({ last_login: new Date() }); + + // Generate JWT token with tenant information + const token = multiAuth.generateJWTToken(user, tenantId); + + // Remove password hash from response + const { password_hash: _, ...userResponse } = user.toJSON(); + + res.json({ + success: true, + data: { + user: userResponse, + token, + expires_in: '24h', + tenant: { + id: tenant.slug, + name: tenant.name + } + }, + message: 'Login successful' + }); } catch (error) { console.error('Local login error:', error); diff --git a/server/routes/user.js b/server/routes/user.js index de9390a..39ac43a 100644 --- a/server/routes/user.js +++ b/server/routes/user.js @@ -3,10 +3,11 @@ const router = express.Router(); const Joi = require('joi'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); -const { User } = require('../models'); +const { User, Tenant } = require('../models'); const { Op } = require('sequelize'); const { validateRequest } = require('../middleware/validation'); const { authenticateToken, requireRole } = require('../middleware/auth'); +const MultiTenantAuth = require('../middleware/multi-tenant-auth'); // Validation schemas const registerSchema = Joi.object({ @@ -80,14 +81,30 @@ router.post('/login', validateRequest(loginSchema), async (req, res) => { try { const { username, password } = req.body; - // Find user by username or email + // Initialize multi-tenant auth + const multiAuth = new MultiTenantAuth(); + + // Determine tenant from request + const tenantId = await multiAuth.determineTenant(req); + + // Find tenant + const tenant = await Tenant.findOne({ where: { slug: tenantId } }); + if (!tenant) { + return res.status(404).json({ + success: false, + message: 'Tenant not found' + }); + } + + // Find user by username or email within this tenant const user = await User.findOne({ where: { [Op.or]: [ { username: username }, { email: username } ], - is_active: true + is_active: true, + tenant_id: tenant.id } }); @@ -101,12 +118,8 @@ router.post('/login', validateRequest(loginSchema), async (req, res) => { // Update last login await user.update({ last_login: new Date() }); - // Generate JWT token - const token = jwt.sign( - { userId: user.id, username: user.username, role: user.role }, - process.env.JWT_SECRET, - { expiresIn: '24h' } - ); + // Generate JWT token with tenant information + const token = multiAuth.generateJWTToken(user, tenantId); // Remove password hash from response const { password_hash: _, ...userResponse } = user.toJSON(); @@ -116,7 +129,11 @@ router.post('/login', validateRequest(loginSchema), async (req, res) => { data: { user: userResponse, token, - expires_in: '24h' + expires_in: '24h', + tenant: { + id: tenant.slug, + name: tenant.name + } }, message: 'Login successful' });