Fix jwt-token
This commit is contained in:
@@ -30,6 +30,15 @@ class MultiTenantAuth {
|
|||||||
this.models = models;
|
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
|
* Initialize all authentication providers
|
||||||
*/
|
*/
|
||||||
@@ -89,13 +98,17 @@ class MultiTenantAuth {
|
|||||||
|
|
||||||
// Method 5: Subdomain (tenant.yourapp.com)
|
// Method 5: Subdomain (tenant.yourapp.com)
|
||||||
const hostname = req.hostname || req.headers.host || '';
|
const hostname = req.hostname || req.headers.host || '';
|
||||||
if (hostname && !hostname.startsWith('localhost')) {
|
// Remove port number if present
|
||||||
const hostParts = hostname.split('.');
|
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)
|
// Only treat as subdomain if there are at least 2 parts (subdomain.domain.com)
|
||||||
// and the first part is not a common root domain
|
// and the first part is not a common root domain
|
||||||
if (hostParts.length >= 3) {
|
if (hostParts.length >= 3) {
|
||||||
const subdomain = hostParts[0];
|
const subdomain = hostParts[0];
|
||||||
if (subdomain && subdomain !== 'www' && subdomain !== 'api' && !subdomain.includes(':')) {
|
if (subdomain && subdomain !== 'www' && subdomain !== 'api') {
|
||||||
console.log('🏢 Tenant from subdomain:', subdomain);
|
console.log('🏢 Tenant from subdomain:', subdomain);
|
||||||
return subdomain;
|
return subdomain;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
function validateRequest(schema, target = 'body') {
|
function validateRequest(schema, source = 'body') {
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
const data = req[target];
|
const dataToValidate = req[source];
|
||||||
const { error, value } = schema.validate(data, {
|
const { error, value } = schema.validate(dataToValidate, {
|
||||||
abortEarly: false,
|
abortEarly: false,
|
||||||
stripUnknown: true
|
stripUnknown: true
|
||||||
});
|
});
|
||||||
@@ -13,20 +13,15 @@ function validateRequest(schema, target = 'body') {
|
|||||||
value: detail.context.value
|
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({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: message,
|
message: 'Validation error',
|
||||||
errors: errorDetails,
|
errors: errorDetails
|
||||||
details: errorDetails // For backward compatibility
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the target data with validated and sanitized data
|
// Replace the validated data source with validated and sanitized data
|
||||||
req[target] = value;
|
req[source] = value;
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ const sinon = require('sinon');
|
|||||||
const request = require('supertest');
|
const request = require('supertest');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const { setupTestEnvironment, teardownTestEnvironment, cleanDatabase, createTestUser, createTestTenant, generateTestToken } = require('../setup');
|
const { setupTestEnvironment, teardownTestEnvironment, cleanDatabase, createTestUser, createTestTenant, generateTestToken } = require('../setup');
|
||||||
const { createTenantRequest, TEST_TENANTS } = require('../utils/testDomains');
|
|
||||||
const authRoutes = require('../../routes/auth');
|
const authRoutes = require('../../routes/auth');
|
||||||
|
|
||||||
describe('Auth Routes', () => {
|
describe('Auth Routes', () => {
|
||||||
@@ -29,18 +28,16 @@ describe('Auth Routes', () => {
|
|||||||
|
|
||||||
describe('POST /auth/login', () => {
|
describe('POST /auth/login', () => {
|
||||||
it('should login with valid credentials', async () => {
|
it('should login with valid credentials', async () => {
|
||||||
const tenant = await createTestTenant({ slug: TEST_TENANTS.DEFAULT });
|
const tenant = await createTestTenant({ slug: 'test-tenant' });
|
||||||
console.log('🔧 TEST: Created tenant:', { id: tenant.id, slug: tenant.slug });
|
|
||||||
|
|
||||||
const user = await createTestUser({
|
const user = await createTestUser({
|
||||||
username: 'testuser',
|
username: 'testuser',
|
||||||
password: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
password: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||||
tenant_id: tenant.id
|
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')
|
.post('/auth/login')
|
||||||
|
.set('Host', 'test-tenant.example.com')
|
||||||
.send({
|
.send({
|
||||||
username: 'testuser',
|
username: 'testuser',
|
||||||
password: 'password'
|
password: 'password'
|
||||||
@@ -55,6 +52,7 @@ describe('Auth Routes', () => {
|
|||||||
it('should reject invalid username', async () => {
|
it('should reject invalid username', async () => {
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.post('/auth/login')
|
.post('/auth/login')
|
||||||
|
.set('Host', 'test-tenant.example.com')
|
||||||
.send({
|
.send({
|
||||||
username: 'nonexistent',
|
username: 'nonexistent',
|
||||||
password: 'password'
|
password: 'password'
|
||||||
@@ -66,13 +64,16 @@ describe('Auth Routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should reject invalid password', async () => {
|
it('should reject invalid password', async () => {
|
||||||
|
const tenant = await createTestTenant({ slug: 'test-tenant' });
|
||||||
const user = await createTestUser({
|
const user = await createTestUser({
|
||||||
username: 'testuser',
|
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)
|
const response = await request(app)
|
||||||
.post('/auth/login')
|
.post('/auth/login')
|
||||||
|
.set('Host', 'test-tenant.example.com')
|
||||||
.send({
|
.send({
|
||||||
username: 'testuser',
|
username: 'testuser',
|
||||||
password: 'wrongpassword'
|
password: 'wrongpassword'
|
||||||
@@ -83,14 +84,17 @@ describe('Auth Routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should reject inactive user', async () => {
|
it('should reject inactive user', async () => {
|
||||||
|
const tenant = await createTestTenant({ slug: 'test-tenant' });
|
||||||
const user = await createTestUser({
|
const user = await createTestUser({
|
||||||
username: 'inactive',
|
username: 'inactive',
|
||||||
password: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
|
password: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
|
||||||
is_active: false
|
is_active: false,
|
||||||
|
tenant_id: tenant.id
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.post('/auth/login')
|
.post('/auth/login')
|
||||||
|
.set('Host', 'test-tenant.example.com')
|
||||||
.send({
|
.send({
|
||||||
username: 'inactive',
|
username: 'inactive',
|
||||||
password: 'password'
|
password: 'password'
|
||||||
@@ -111,6 +115,7 @@ describe('Auth Routes', () => {
|
|||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.post('/auth/login')
|
.post('/auth/login')
|
||||||
|
.set('Host', 'test-tenant.example.com')
|
||||||
.send({
|
.send({
|
||||||
username: 'testuser',
|
username: 'testuser',
|
||||||
password: 'password'
|
password: 'password'
|
||||||
@@ -123,8 +128,11 @@ describe('Auth Routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should validate required fields', async () => {
|
it('should validate required fields', async () => {
|
||||||
|
const tenant = await createTestTenant({ slug: 'test-tenant' });
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.post('/auth/login')
|
.post('/auth/login')
|
||||||
|
.set('Host', 'test-tenant.example.com')
|
||||||
.send({
|
.send({
|
||||||
username: 'testuser'
|
username: 'testuser'
|
||||||
// missing password
|
// missing password
|
||||||
@@ -135,12 +143,15 @@ describe('Auth Routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle database errors gracefully', async () => {
|
it('should handle database errors gracefully', async () => {
|
||||||
|
const tenant = await createTestTenant({ slug: 'test-tenant' });
|
||||||
|
|
||||||
// Mock database error
|
// Mock database error
|
||||||
const originalFindOne = models.User.findOne;
|
const originalFindOne = models.User.findOne;
|
||||||
models.User.findOne = sinon.stub().rejects(new Error('Database error'));
|
models.User.findOne = sinon.stub().rejects(new Error('Database error'));
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.post('/auth/login')
|
.post('/auth/login')
|
||||||
|
.set('Host', 'test-tenant.example.com')
|
||||||
.send({
|
.send({
|
||||||
username: 'testuser',
|
username: 'testuser',
|
||||||
password: 'password'
|
password: 'password'
|
||||||
|
|||||||
Reference in New Issue
Block a user