diff --git a/management/src/contexts/AuthContext.jsx b/management/src/contexts/AuthContext.jsx index cbfd88d..f3cc48e 100644 --- a/management/src/contexts/AuthContext.jsx +++ b/management/src/contexts/AuthContext.jsx @@ -17,21 +17,36 @@ export const AuthProvider = ({ children }) => { const [loading, setLoading] = useState(true) useEffect(() => { - // Check for existing token on app start + // Check for existing token on app start and validate it + checkAuthStatus() + }, []) + + const checkAuthStatus = async () => { const token = localStorage.getItem('management_token') const savedUser = localStorage.getItem('management_user') - if (token && savedUser) { - try { - setUser(JSON.parse(savedUser)) - } catch (error) { - console.error('Error parsing saved user:', error) - localStorage.removeItem('management_token') - localStorage.removeItem('management_user') - } + if (!token || !savedUser) { + setLoading(false) + return } - setLoading(false) - }, []) + + try { + // Validate token by making a simple API call + const response = await api.get('/management/tenants?limit=1') + // If successful, use saved user data + const parsedUser = JSON.parse(savedUser) + setUser(parsedUser) + console.log('✅ Management token validated for user:', parsedUser.username) + } catch (error) { + console.warn('🔓 Management token validation failed:', error.response?.status, error.response?.data?.message) + // Clear invalid auth data (but don't redirect here, let the api interceptor handle it) + localStorage.removeItem('management_token') + localStorage.removeItem('management_user') + setUser(null) + } finally { + setLoading(false) + } + } const login = async (username, password) => { try { @@ -73,6 +88,7 @@ export const AuthProvider = ({ children }) => { loading, login, logout, + checkAuthStatus, isAuthenticated: !!user, isAdmin: user?.role === 'admin' || user?.role === 'super_admin' || user?.role === 'platform_admin', isSuperAdmin: user?.role === 'super_admin', diff --git a/server/routes/dataRetention.js b/server/routes/dataRetention.js index 0828c91..50bf282 100644 --- a/server/routes/dataRetention.js +++ b/server/routes/dataRetention.js @@ -32,10 +32,11 @@ const requireManagementAuth = async (req, res, next) => { }); } - // Verify JWT token + // Verify JWT token using management secret let decoded; try { - decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-in-production'); + const MANAGEMENT_SECRET = process.env.MANAGEMENT_JWT_SECRET || 'mgmt-super-secret-change-in-production'; + decoded = jwt.verify(token, MANAGEMENT_SECRET); } catch (jwtError) { await auditLogger.logAuthFailure(req, req.path, `Invalid JWT token: ${jwtError.message}`); return res.status(401).json({ @@ -45,8 +46,8 @@ const requireManagementAuth = async (req, res, next) => { }); } - // Verify this is a management user - if (decoded.type !== 'management') { + // Verify this is a management user with proper role + if (!decoded.isManagement || !['super_admin', 'platform_admin'].includes(decoded.role)) { await auditLogger.logPermissionDenied(null, req, req.path, 'Not a management user token'); return res.status(403).json({ success: false, @@ -56,7 +57,7 @@ const requireManagementAuth = async (req, res, next) => { } // Verify user still exists and is active - const managementUser = await ManagementUser.findByPk(decoded.id); + const managementUser = await ManagementUser.findByPk(decoded.userId); if (!managementUser || !managementUser.is_active) { await auditLogger.logPermissionDenied(decoded, req, req.path, 'Management user not found or inactive'); return res.status(403).json({ diff --git a/server/utils/dataRetentionAuditLogger.js b/server/utils/dataRetentionAuditLogger.js index 0b060bd..f7aa30a 100644 --- a/server/utils/dataRetentionAuditLogger.js +++ b/server/utils/dataRetentionAuditLogger.js @@ -10,6 +10,7 @@ class DataRetentionAuditLogger { constructor() { this.logDir = process.env.SECURITY_LOG_DIR || './logs'; this.logFile = path.join(this.logDir, 'data_retention_access.log'); + this.loggedWriteError = false; // Flag to avoid spamming write error messages } async ensureLogDir() { @@ -49,11 +50,23 @@ class DataRetentionAuditLogger { } catch (writeError) { // Fallback to console logging if file writing fails (e.g., permission issues) console.log('[DATA_RETENTION_AUDIT]', JSON.stringify(logEntry)); + + // Only log the file write error once to avoid spam + if (!this.loggedWriteError) { + console.warn('Failed to write security log to file, using console logging:', writeError.message); + this.loggedWriteError = true; + } } } catch (error) { // Fallback to console logging for any errors - console.log('[DATA_RETENTION_AUDIT_ERROR]', error.message, JSON.stringify(event, null, 2)); + console.log('[DATA_RETENTION_AUDIT_ERROR]', error.message); + console.log('[DATA_RETENTION_AUDIT_FALLBACK]', JSON.stringify({ + timestamp: new Date().toISOString(), + event: event.type || 'UNKNOWN', + result: event.result || 'error', + error: event.error || error.message + })); } }