Fix jwt-token
This commit is contained in:
@@ -204,6 +204,38 @@ router.post('/login', async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /auth/register
|
||||||
|
* Universal registration endpoint that routes to appropriate provider
|
||||||
|
*/
|
||||||
|
router.post('/register', async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
// Determine tenant
|
||||||
|
const tenantId = await multiAuth.determineTenant(req);
|
||||||
|
const authConfig = await multiAuth.getTenantAuthConfig(tenantId);
|
||||||
|
|
||||||
|
req.tenant = { id: tenantId, authConfig };
|
||||||
|
|
||||||
|
// Only local authentication supports registration
|
||||||
|
if (authConfig.type !== 'local') {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: `Registration not supported for ${authConfig.type} authentication`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route to local registration handler
|
||||||
|
return require('../routes/user').registerLocal(req, res, next);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Registration error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Registration failed'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /auth/local
|
* POST /auth/local
|
||||||
* Local authentication endpoint with tenant isolation
|
* Local authentication endpoint with tenant isolation
|
||||||
@@ -478,4 +510,143 @@ router.post('/test/:tenantId', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /auth/refresh
|
||||||
|
* Refresh JWT token
|
||||||
|
*/
|
||||||
|
router.post('/refresh', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Authorization token required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = authHeader.substring(7);
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
// Verify current token (even if expired, we want to check if it's valid)
|
||||||
|
let decoded;
|
||||||
|
try {
|
||||||
|
decoded = jwt.verify(token, process.env.JWT_SECRET, { ignoreExpiration: true });
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Invalid token'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find user to ensure they still exist and are active
|
||||||
|
const { User } = require('../models');
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: { id: decoded.userId, is_active: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
message: 'User not found or inactive'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate new token
|
||||||
|
const newToken = jwt.sign(
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
username: user.username,
|
||||||
|
role: user.role,
|
||||||
|
tenantId: decoded.tenantId,
|
||||||
|
provider: user.auth_provider
|
||||||
|
},
|
||||||
|
process.env.JWT_SECRET,
|
||||||
|
{ expiresIn: '24h' }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
token: newToken,
|
||||||
|
expires_in: '24h'
|
||||||
|
},
|
||||||
|
message: 'Token refreshed successfully'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Token refresh error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Token refresh failed'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /auth/logout
|
||||||
|
* Logout (mainly for client-side token removal)
|
||||||
|
*/
|
||||||
|
router.post('/logout', (req, res) => {
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Logged out successfully'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /auth/me
|
||||||
|
* Get current user information
|
||||||
|
*/
|
||||||
|
router.get('/me', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Authorization token required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = authHeader.substring(7);
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
let decoded;
|
||||||
|
try {
|
||||||
|
decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Invalid or expired token'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find user
|
||||||
|
const { User } = require('../models');
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: { id: decoded.userId, is_active: true },
|
||||||
|
attributes: { exclude: ['password_hash'] }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
message: 'User not found or inactive'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: { user },
|
||||||
|
message: 'User information retrieved successfully'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Get user info error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Failed to retrieve user information'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ const multiAuth = new MultiTenantAuth();
|
|||||||
*/
|
*/
|
||||||
router.get('/', authenticateToken, async (req, res) => {
|
router.get('/', authenticateToken, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// Determine tenant from request
|
// Get tenant from authenticated user context
|
||||||
const tenantId = await multiAuth.determineTenant(req);
|
const tenantId = req.tenantId;
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Unable to determine tenant'
|
message: 'No tenant context available'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +32,13 @@ router.get('/', authenticateToken, async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tenant.is_active) {
|
||||||
|
return res.status(403).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Tenant is inactive'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
device_id,
|
device_id,
|
||||||
drone_id,
|
drone_id,
|
||||||
@@ -236,12 +243,12 @@ router.get('/debug', authenticateToken, async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/:id', authenticateToken, async (req, res) => {
|
router.get('/:id', authenticateToken, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// Determine tenant from request
|
// Get tenant from authenticated user context
|
||||||
const tenantId = await multiAuth.determineTenant(req);
|
const tenantId = req.tenantId;
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Unable to determine tenant'
|
message: 'No tenant context available'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +260,13 @@ router.get('/:id', authenticateToken, async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tenant.is_active) {
|
||||||
|
return res.status(403).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Tenant is inactive'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
const detection = await DroneDetection.findByPk(id, {
|
const detection = await DroneDetection.findByPk(id, {
|
||||||
@@ -294,24 +308,62 @@ router.delete('/:id', authenticateToken, async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
// Check if user is admin
|
// Check if user is admin
|
||||||
if (req.user.role !== 'admin') {
|
if (req.user.role !== 'admin') {
|
||||||
return res.status(403).json({ error: 'Admin access required' });
|
return res.status(403).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Admin access required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get tenant from authenticated user context
|
||||||
|
const tenantId = req.tenantId;
|
||||||
|
if (!tenantId) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'No tenant context available'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const tenant = await Tenant.findOne({ where: { slug: tenantId } });
|
||||||
|
if (!tenant) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Tenant not found'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
const detection = await DroneDetection.findByPk(id);
|
// Find detection with tenant filtering
|
||||||
|
const detection = await DroneDetection.findOne({
|
||||||
|
where: { id },
|
||||||
|
include: [{
|
||||||
|
model: Device,
|
||||||
|
as: 'device',
|
||||||
|
where: { tenant_id: tenant.id }, // Ensure detection belongs to user's tenant
|
||||||
|
attributes: ['id', 'tenant_id']
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
if (!detection) {
|
if (!detection) {
|
||||||
return res.status(404).json({ error: 'Detection not found' });
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Detection not found or not accessible'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await detection.destroy();
|
await detection.destroy();
|
||||||
res.json({ message: 'Detection deleted successfully' });
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Detection deleted successfully'
|
||||||
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting detection:', error);
|
console.error('Error deleting detection:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
error: 'Failed to delete detection',
|
success: false,
|
||||||
details: error.message
|
message: 'Failed to delete detection',
|
||||||
|
error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -84,12 +84,19 @@ const updateProfileSchema = Joi.object({
|
|||||||
|
|
||||||
// POST /api/users/register - Register new user (ULTRA SECURE)
|
// POST /api/users/register - Register new user (ULTRA SECURE)
|
||||||
router.post('/register', registrationLimiter, validateRequest(registerSchema), async (req, res) => {
|
router.post('/register', registrationLimiter, validateRequest(registerSchema), async (req, res) => {
|
||||||
|
return registerLocal(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register local user - can be called from both direct route and auth route
|
||||||
|
*/
|
||||||
|
async function registerLocal(req, res) {
|
||||||
try {
|
try {
|
||||||
console.log('🔒 Registration attempt started');
|
console.log('🔒 Registration attempt started');
|
||||||
|
|
||||||
// Step 1: Determine tenant context - CRITICAL SECURITY CHECK
|
// Step 1: Determine tenant context - CRITICAL SECURITY CHECK
|
||||||
const multiAuth = new MultiTenantAuth();
|
const multiAuth = new MultiTenantAuth();
|
||||||
const tenantId = await multiAuth.determineTenant(req);
|
const tenantId = req.tenant?.id || await multiAuth.determineTenant(req);
|
||||||
console.log('🔍 Registration - Determined tenant:', tenantId);
|
console.log('🔍 Registration - Determined tenant:', tenantId);
|
||||||
|
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
@@ -138,7 +145,7 @@ router.post('/register', registrationLimiter, validateRequest(registerSchema), a
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 6: Additional security - Check if registration is explicitly enabled in auth config
|
// Step 6: Additional security - Check if registration is explicitly enabled in auth config
|
||||||
const authConfig = await multiAuth.getTenantAuthConfig(tenantId);
|
const authConfig = req.tenant?.authConfig || await multiAuth.getTenantAuthConfig(tenantId);
|
||||||
if (!authConfig.enabled) {
|
if (!authConfig.enabled) {
|
||||||
console.log('❌ Registration BLOCKED - Auth config disabled');
|
console.log('❌ Registration BLOCKED - Auth config disabled');
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
@@ -192,7 +199,7 @@ router.post('/register', registrationLimiter, validateRequest(registerSchema), a
|
|||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: userResponse,
|
data: { user: userResponse },
|
||||||
message: 'User registered successfully'
|
message: 'User registered successfully'
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -212,7 +219,7 @@ router.post('/register', registrationLimiter, validateRequest(registerSchema), a
|
|||||||
error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error'
|
error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// POST /api/users/login - User login (direct API access - for legacy support)
|
// POST /api/users/login - User login (direct API access - for legacy support)
|
||||||
router.post('/login', validateRequest(loginSchema), async (req, res) => {
|
router.post('/login', validateRequest(loginSchema), async (req, res) => {
|
||||||
@@ -385,6 +392,20 @@ router.get('/', authenticateToken, requireRole(['admin']), async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
async function loginLocal(req, res, next) {
|
async function loginLocal(req, res, next) {
|
||||||
try {
|
try {
|
||||||
|
// Validate required fields
|
||||||
|
if (!req.body.username || !req.body.password) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Validation error for field' +
|
||||||
|
(!req.body.username && !req.body.password ? 's: username, password' :
|
||||||
|
!req.body.username ? ': username' : ': password'),
|
||||||
|
details: [
|
||||||
|
...(!req.body.username ? [{ field: 'username', message: '"username" is required' }] : []),
|
||||||
|
...(!req.body.password ? [{ field: 'password', message: '"password" is required' }] : [])
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
|
|
||||||
// Get tenant information from request (set by multi-tenant auth middleware)
|
// Get tenant information from request (set by multi-tenant auth middleware)
|
||||||
@@ -458,8 +479,8 @@ async function loginLocal(req, res, next) {
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
tenantId: user.tenant_id,
|
tenantId: req.tenant?.id, // This should be the tenant slug
|
||||||
tenantSlug: req.tenant?.id
|
provider: user.auth_provider
|
||||||
},
|
},
|
||||||
process.env.JWT_SECRET,
|
process.env.JWT_SECRET,
|
||||||
{ expiresIn: '24h' }
|
{ expiresIn: '24h' }
|
||||||
@@ -490,3 +511,4 @@ async function loginLocal(req, res, next) {
|
|||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
module.exports.loginLocal = loginLocal;
|
module.exports.loginLocal = loginLocal;
|
||||||
|
module.exports.registerLocal = registerLocal;
|
||||||
|
|||||||
Reference in New Issue
Block a user