Fix jwt-token
This commit is contained in:
@@ -3,21 +3,65 @@ const router = express.Router();
|
||||
const Joi = require('joi');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const rateLimit = require('express-rate-limit');
|
||||
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
|
||||
// Rate limiting for registration endpoint - EXTRA SECURITY
|
||||
const registrationLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 3, // Limit each IP to 3 registration attempts per windowMs
|
||||
message: {
|
||||
success: false,
|
||||
message: 'Too many registration attempts. Please try again later.'
|
||||
},
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
|
||||
// Enhanced validation schema with stronger requirements
|
||||
const registerSchema = Joi.object({
|
||||
username: Joi.string().min(3).max(50).required(),
|
||||
email: Joi.string().email().required(),
|
||||
password: Joi.string().min(6).required(),
|
||||
first_name: Joi.string().optional(),
|
||||
last_name: Joi.string().optional(),
|
||||
phone_number: Joi.string().optional(),
|
||||
role: Joi.string().valid('admin', 'operator', 'viewer').default('viewer')
|
||||
username: Joi.string()
|
||||
.min(3)
|
||||
.max(50)
|
||||
.pattern(/^[a-zA-Z0-9._-]+$/)
|
||||
.required()
|
||||
.messages({
|
||||
'string.pattern.base': 'Username can only contain letters, numbers, dots, underscores, and hyphens'
|
||||
}),
|
||||
email: Joi.string()
|
||||
.email()
|
||||
.required()
|
||||
.max(255),
|
||||
password: Joi.string()
|
||||
.min(8)
|
||||
.max(100)
|
||||
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
|
||||
.required()
|
||||
.messages({
|
||||
'string.pattern.base': 'Password must contain at least one lowercase letter, one uppercase letter, and one number'
|
||||
}),
|
||||
first_name: Joi.string()
|
||||
.max(100)
|
||||
.optional()
|
||||
.allow(''),
|
||||
last_name: Joi.string()
|
||||
.max(100)
|
||||
.optional()
|
||||
.allow(''),
|
||||
phone_number: Joi.string()
|
||||
.pattern(/^[\+]?[1-9][\d]{0,15}$/)
|
||||
.optional()
|
||||
.allow('')
|
||||
.messages({
|
||||
'string.pattern.base': 'Please enter a valid phone number'
|
||||
}),
|
||||
role: Joi.string()
|
||||
.valid('viewer') // Only allow viewer role for self-registration
|
||||
.default('viewer')
|
||||
});
|
||||
|
||||
const loginSchema = Joi.object({
|
||||
@@ -34,21 +78,111 @@ const updateProfileSchema = Joi.object({
|
||||
timezone: Joi.string().optional()
|
||||
});
|
||||
|
||||
// POST /api/users/register - Register new user
|
||||
router.post('/register', validateRequest(registerSchema), async (req, res) => {
|
||||
// POST /api/users/register - Register new user (ULTRA SECURE)
|
||||
router.post('/register', registrationLimiter, validateRequest(registerSchema), async (req, res) => {
|
||||
try {
|
||||
console.log('🔒 Registration attempt started');
|
||||
|
||||
// Step 1: Determine tenant context - CRITICAL SECURITY CHECK
|
||||
const multiAuth = new MultiTenantAuth();
|
||||
const tenantId = await multiAuth.determineTenant(req);
|
||||
console.log('🔍 Registration - Determined tenant:', tenantId);
|
||||
|
||||
if (!tenantId) {
|
||||
console.log('❌ Registration BLOCKED - No tenant determined');
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Unable to determine tenant context'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 2: Get tenant from database - VERIFY TENANT EXISTS
|
||||
const tenant = await Tenant.findOne({ where: { slug: tenantId } });
|
||||
if (!tenant) {
|
||||
console.log('❌ Registration BLOCKED - Tenant not found:', tenantId);
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Tenant not found'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 3: TRIPLE CHECK - Tenant must be active
|
||||
if (!tenant.is_active) {
|
||||
console.log('❌ Registration BLOCKED - Tenant inactive:', tenantId);
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Registration not available for this tenant'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 4: CRITICAL SECURITY CHECK - Only local auth tenants allow registration
|
||||
if (tenant.auth_provider !== 'local') {
|
||||
console.log('❌ Registration BLOCKED - Non-local auth provider:', tenant.auth_provider);
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Registration not available. This tenant uses external authentication.'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 5: ULTIMATE SECURITY CHECK - Explicit registration permission
|
||||
if (!tenant.allow_registration) {
|
||||
console.log('❌ Registration BLOCKED - Registration disabled for tenant:', tenantId);
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Registration is not enabled for this tenant. Please contact your administrator.'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 6: Additional security - Check if registration is explicitly enabled in auth config
|
||||
const authConfig = await multiAuth.getTenantAuthConfig(tenantId);
|
||||
if (!authConfig.enabled) {
|
||||
console.log('❌ Registration BLOCKED - Auth config disabled');
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Authentication is disabled for this tenant'
|
||||
});
|
||||
}
|
||||
|
||||
console.log('✅ Registration security checks passed for tenant:', tenantId);
|
||||
|
||||
// Step 7: Process registration - User data validation
|
||||
const { password, ...userData } = req.body;
|
||||
|
||||
// Hash password
|
||||
// Check if user already exists in this tenant
|
||||
const existingUser = await User.findOne({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{ username: userData.username },
|
||||
{ email: userData.email }
|
||||
],
|
||||
tenant_id: tenant.id // Scope to specific tenant
|
||||
}
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
console.log('❌ Registration BLOCKED - User already exists in tenant');
|
||||
return res.status(409).json({
|
||||
success: false,
|
||||
message: 'Username or email already exists'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 8: Hash password with high security
|
||||
const saltRounds = 12;
|
||||
const password_hash = await bcrypt.hash(password, saltRounds);
|
||||
|
||||
// Create user
|
||||
// Step 9: Create user with tenant association
|
||||
const user = await User.create({
|
||||
...userData,
|
||||
password_hash
|
||||
password_hash,
|
||||
tenant_id: tenant.id, // CRITICAL: Associate with specific tenant
|
||||
is_active: true,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
});
|
||||
|
||||
console.log('✅ User registered successfully:', user.username, 'for tenant:', tenantId);
|
||||
|
||||
// Remove password hash from response
|
||||
const { password_hash: _, ...userResponse } = user.toJSON();
|
||||
|
||||
@@ -59,7 +193,7 @@ router.post('/register', validateRequest(registerSchema), async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error registering user:', error);
|
||||
console.error('❌ Registration error:', error);
|
||||
|
||||
if (error.name === 'SequelizeUniqueConstraintError') {
|
||||
return res.status(409).json({
|
||||
@@ -70,7 +204,7 @@ router.post('/register', validateRequest(registerSchema), async (req, res) => {
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to register user',
|
||||
message: 'Registration failed',
|
||||
error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error'
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user