Initial commit: StreamFlow IPTV platform

This commit is contained in:
aiulian25 2025-12-17 00:42:43 +00:00
commit 73a8ae9ffd
1240 changed files with 278451 additions and 0 deletions

129
backend/middleware/auth.js Normal file
View file

@ -0,0 +1,129 @@
const jwt = require('jsonwebtoken');
const logger = require('../utils/logger');
const db = require('../database/db').db;
const { SESSION_POLICY } = require('../utils/passwordPolicy');
const JWT_SECRET = process.env.JWT_SECRET || 'change_this_in_production';
const authenticate = (req, res, next) => {
// Check Authorization header first, then query parameter
let token = req.headers.authorization?.split(' ')[1];
if (!token && req.query.token) {
token = req.query.token;
}
if (!token) {
logger.info('[AUTH] No token provided');
return res.status(401).json({ error: 'Authentication required' });
}
// CWE-532: Do not log tokens or token details - they are credentials
logger.info('[AUTH] Verifying authentication token');
try {
const decoded = jwt.verify(token, JWT_SECRET);
logger.info(`[AUTH] Token verified for user ${decoded.userId}`);
// Check session activity and idle timeout
db.get(
'SELECT * FROM active_sessions WHERE session_token = ? AND user_id = ?',
[token, decoded.userId],
(err, session) => {
if (err) {
logger.error('Session check error:', err);
return res.status(500).json({ error: 'Session validation failed' });
}
if (!session) {
logger.info('[AUTH] Session not found for token in database');
return res.status(401).json({ error: 'Session not found or expired', sessionExpired: true });
}
logger.info('[AUTH] Session found, checking expiry');
// Check if session has expired (absolute timeout)
const now = new Date();
const expiresAt = new Date(session.expires_at);
if (now >= expiresAt) {
// Delete expired session
db.run('DELETE FROM active_sessions WHERE id = ?', [session.id]);
return res.status(401).json({ error: 'Session expired', sessionExpired: true });
}
// Check idle timeout (2 hours by default)
const lastActivity = new Date(session.last_activity);
const idleTimeMs = now - lastActivity;
const idleTimeoutMs = SESSION_POLICY.idleTimeout * 60 * 60 * 1000; // Convert hours to ms
if (idleTimeMs > idleTimeoutMs) {
// Session idle for too long - terminate it
db.run('DELETE FROM active_sessions WHERE id = ?', [session.id]);
logger.info(`Session ${session.id} terminated due to idle timeout (${idleTimeMs}ms idle)`);
return res.status(401).json({ error: 'Session expired due to inactivity', sessionExpired: true });
}
// Update last activity
db.run(
'UPDATE active_sessions SET last_activity = ? WHERE id = ?',
[now.toISOString(), session.id],
(updateErr) => {
if (updateErr) {
logger.error('Failed to update session activity:', updateErr);
}
}
);
req.user = decoded;
req.sessionId = session.id;
next();
}
);
} catch (error) {
logger.error('Authentication error:', error);
logger.error(`[AUTH] JWT Verification Failed: ${error.name} - ${error.message}`);
// Provide more specific error messages
let errorMessage = 'Invalid or expired token';
if (error.name === 'TokenExpiredError') {
errorMessage = 'Token has expired';
} else if (error.name === 'JsonWebTokenError') {
errorMessage = 'Invalid token';
}
res.status(401).json({
error: errorMessage,
sessionExpired: true // This triggers automatic logout on frontend
});
}
};
const authorize = (...roles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
};
// Convenience middleware for admin-only routes
const requireAdmin = (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Admin access required' });
}
next();
};
module.exports = { authenticate, authorize, requireAdmin };