From f8fcfbb5be422e3efc758986d343f1bbb88aea85 Mon Sep 17 00:00:00 2001 From: Alexander Borg Date: Tue, 16 Sep 2025 08:06:50 +0200 Subject: [PATCH] Fix jwt-token --- server/middleware/multi-tenant-auth.js | 19 +++++++++++++++--- server/middleware/validation.js | 19 +++++++----------- server/tests/routes/auth.test.js | 27 ++++++++++++++++++-------- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/server/middleware/multi-tenant-auth.js b/server/middleware/multi-tenant-auth.js index cc694a5..21121f2 100644 --- a/server/middleware/multi-tenant-auth.js +++ b/server/middleware/multi-tenant-auth.js @@ -30,6 +30,15 @@ class MultiTenantAuth { this.models = models; } + /** + * Check if a string is an IP address + */ + isIPAddress(str) { + const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/; + return ipv4Regex.test(str) || ipv6Regex.test(str); + } + /** * Initialize all authentication providers */ @@ -89,13 +98,17 @@ class MultiTenantAuth { // Method 5: Subdomain (tenant.yourapp.com) const hostname = req.hostname || req.headers.host || ''; - if (hostname && !hostname.startsWith('localhost')) { - const hostParts = hostname.split('.'); + // Remove port number if present + const hostWithoutPort = hostname.split(':')[0]; + + // Skip if localhost or IP address + if (hostname && !hostname.startsWith('localhost') && !this.isIPAddress(hostWithoutPort)) { + const hostParts = hostWithoutPort.split('.'); // Only treat as subdomain if there are at least 2 parts (subdomain.domain.com) // and the first part is not a common root domain if (hostParts.length >= 3) { const subdomain = hostParts[0]; - if (subdomain && subdomain !== 'www' && subdomain !== 'api' && !subdomain.includes(':')) { + if (subdomain && subdomain !== 'www' && subdomain !== 'api') { console.log('🏢 Tenant from subdomain:', subdomain); return subdomain; } diff --git a/server/middleware/validation.js b/server/middleware/validation.js index fc69ca6..7ed54ea 100644 --- a/server/middleware/validation.js +++ b/server/middleware/validation.js @@ -1,7 +1,7 @@ -function validateRequest(schema, target = 'body') { +function validateRequest(schema, source = 'body') { return (req, res, next) => { - const data = req[target]; - const { error, value } = schema.validate(data, { + const dataToValidate = req[source]; + const { error, value } = schema.validate(dataToValidate, { abortEarly: false, stripUnknown: true }); @@ -13,20 +13,15 @@ function validateRequest(schema, target = 'body') { value: detail.context.value })); - // Create a message that includes the field names for test compatibility - const fieldNames = errorDetails.map(err => err.field).join(', '); - const message = `Validation error: ${fieldNames}`; - return res.status(400).json({ success: false, - message: message, - errors: errorDetails, - details: errorDetails // For backward compatibility + message: 'Validation error', + errors: errorDetails }); } - // Replace the target data with validated and sanitized data - req[target] = value; + // Replace the validated data source with validated and sanitized data + req[source] = value; next(); }; } diff --git a/server/tests/routes/auth.test.js b/server/tests/routes/auth.test.js index 41b2fc5..4395965 100644 --- a/server/tests/routes/auth.test.js +++ b/server/tests/routes/auth.test.js @@ -4,7 +4,6 @@ const sinon = require('sinon'); const request = require('supertest'); const express = require('express'); const { setupTestEnvironment, teardownTestEnvironment, cleanDatabase, createTestUser, createTestTenant, generateTestToken } = require('../setup'); -const { createTenantRequest, TEST_TENANTS } = require('../utils/testDomains'); const authRoutes = require('../../routes/auth'); describe('Auth Routes', () => { @@ -29,18 +28,16 @@ describe('Auth Routes', () => { describe('POST /auth/login', () => { it('should login with valid credentials', async () => { - const tenant = await createTestTenant({ slug: TEST_TENANTS.DEFAULT }); - console.log('🔧 TEST: Created tenant:', { id: tenant.id, slug: tenant.slug }); - + const tenant = await createTestTenant({ slug: 'test-tenant' }); const user = await createTestUser({ username: 'testuser', password: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password tenant_id: tenant.id }); - console.log('🔧 TEST: Created user:', { id: user.id, username: user.username, tenant_id: user.tenant_id }); - const response = await createTenantRequest(request, app, TEST_TENANTS.DEFAULT) + const response = await request(app) .post('/auth/login') + .set('Host', 'test-tenant.example.com') .send({ username: 'testuser', password: 'password' @@ -55,6 +52,7 @@ describe('Auth Routes', () => { it('should reject invalid username', async () => { const response = await request(app) .post('/auth/login') + .set('Host', 'test-tenant.example.com') .send({ username: 'nonexistent', password: 'password' @@ -66,13 +64,16 @@ describe('Auth Routes', () => { }); it('should reject invalid password', async () => { + const tenant = await createTestTenant({ slug: 'test-tenant' }); const user = await createTestUser({ username: 'testuser', - password: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi' + password: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + tenant_id: tenant.id }); const response = await request(app) .post('/auth/login') + .set('Host', 'test-tenant.example.com') .send({ username: 'testuser', password: 'wrongpassword' @@ -83,14 +84,17 @@ describe('Auth Routes', () => { }); it('should reject inactive user', async () => { + const tenant = await createTestTenant({ slug: 'test-tenant' }); const user = await createTestUser({ username: 'inactive', password: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', - is_active: false + is_active: false, + tenant_id: tenant.id }); const response = await request(app) .post('/auth/login') + .set('Host', 'test-tenant.example.com') .send({ username: 'inactive', password: 'password' @@ -111,6 +115,7 @@ describe('Auth Routes', () => { const response = await request(app) .post('/auth/login') + .set('Host', 'test-tenant.example.com') .send({ username: 'testuser', password: 'password' @@ -123,8 +128,11 @@ describe('Auth Routes', () => { }); it('should validate required fields', async () => { + const tenant = await createTestTenant({ slug: 'test-tenant' }); + const response = await request(app) .post('/auth/login') + .set('Host', 'test-tenant.example.com') .send({ username: 'testuser' // missing password @@ -135,12 +143,15 @@ describe('Auth Routes', () => { }); it('should handle database errors gracefully', async () => { + const tenant = await createTestTenant({ slug: 'test-tenant' }); + // Mock database error const originalFindOne = models.User.findOne; models.User.findOne = sinon.stub().rejects(new Error('Database error')); const response = await request(app) .post('/auth/login') + .set('Host', 'test-tenant.example.com') .send({ username: 'testuser', password: 'password'