const jwt = require('jsonwebtoken'); const { logger } = require('./logger'); const UnitOfWork = require('../database/UnitOfWork'); const UserStatusRepository = require('../repositories/status/UserStatusRepository'); const UserSettingsRepository = require('../repositories/settings/UserSettingsRepository'); const { normalizeLanguageCode } = require('../utils/languageUtils'); async function authMiddleware(req, res, next) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { logger.warn('authMiddleware:missingToken', { method: req.method, route: req.originalUrl }); return res.status(401).json({ success: false, message: 'No access token provided' }); } const token = authHeader.split(' ')[1]; try { const payload = jwt.verify(token, process.env.JWT_SECRET); // edit profile context awareness const isEditProfile = req.originalUrl?.includes('/profile/personal') || req.baseUrl?.includes('/profile/personal'); // derive and log user type data const derivedUserType = payload.userType ?? payload.user_type ?? payload.type; const authDebug = { context: isEditProfile ? 'edit-profile' : 'general', method: req.method, route: req.originalUrl, id: payload.id ?? payload.userId ?? payload.sub, email: payload.email, userType: payload.userType, user_type: payload.user_type, derivedUserType, payloadKeys: Object.keys(payload || {}) }; logger.info(`authMiddleware:verified context=${authDebug.context} userType=${authDebug.derivedUserType}`, authDebug); // console fallback for local dev console.log('[authMiddleware] verified', authDebug); // Normalize common user fields for downstream checks const normalizedUserId = payload.userId ?? payload.id ?? payload.sub; const normalizedRole = payload.role ?? payload.userRole ?? payload.user_role ?? payload.type; // Attach user info to request (with normalized userType for downstream checks) req.user = { ...payload, userId: normalizedUserId, role: normalizedRole ?? payload.role, userType: payload.userType ?? payload.user_type, user_type: payload.user_type ?? payload.userType }; // Block suspended users try { const unitOfWork = new UnitOfWork(); await unitOfWork.start(); unitOfWork.registerRepository('status', new UserStatusRepository(unitOfWork)); unitOfWork.registerRepository('settings', new UserSettingsRepository(unitOfWork)); const statusRepo = unitOfWork.getRepository('status'); const settingsRepo = unitOfWork.getRepository('settings'); const userStatus = await statusRepo.getStatusByUserId(normalizedUserId); const userSettings = await settingsRepo.getSettingsByUserId(normalizedUserId, unitOfWork); await unitOfWork.commit(); const preferredLanguage = normalizeLanguageCode( userSettings?.preferred_language || userSettings?.preferredLanguage || req.user?.preferred_language || req.user?.preferredLanguage || req.user?.language || req.user?.lang ) || null; if (preferredLanguage) { req.user.preferred_language = preferredLanguage; req.user.preferredLanguage = preferredLanguage; req.user.language = preferredLanguage; req.user.lang = preferredLanguage; } if (userStatus && userStatus.status === 'suspended') { logger.warn('authMiddleware:user_suspended', { userId: normalizedUserId, route: req.originalUrl }); return res.status(403).json({ success: false, message: 'Account suspended' }); } } catch (statusError) { logger.error('authMiddleware:statusCheckFailed', { userId: normalizedUserId, route: req.originalUrl, error: statusError?.message }); return res.status(500).json({ success: false, message: 'Internal server error' }); } // Guest restriction: guest users can only access abo-related routes if (normalizedRole === 'guest') { const guestRestriction = require('./guestRestriction'); return guestRestriction(req, res, next); } next(); } catch (error) { logger.warn('authMiddleware:tokenInvalid', { method: req.method, route: req.originalUrl, reason: error?.message }); return res.status(401).json({ success: false, message: 'Invalid or expired access token' }); } } module.exports = authMiddleware;