import jwt from 'jsonwebtoken';
import { userDb, appConfigDb } from '../database/db.js';
import { IS_PLATFORM, DISABLE_LOCAL_AUTH } from '../constants/config.js';
const JWT_SECRET = process.env.JWT_SECRET || appConfigDb.getOrCreateJwtSecret();
const validateApiKey = (req, res, next) => {
if (!process.env.API_KEY) {
return next();
}
const apiKey = req.headers['x-api-key'];
if (apiKey !== process.env.API_KEY) {
return res.status(401).json({ error: 'Invalid API key' });
}
next();
};
function extractDashboardRefererToken(req) {
const refererHeader = req.headers.referer || req.headers.referrer;
if (!refererHeader || Array.isArray(refererHeader)) {
return null;
}
try {
const refererUrl = new URL(
refererHeader,
`${req.protocol}://${req.get('host')}`,
);
if (!refererUrl.pathname.startsWith('/memory-dashboard')) {
return null;
}
return refererUrl.searchParams.get('token');
} catch {
return null;
}
}
const authenticateToken = async (req, res, next) => {
if (IS_PLATFORM || DISABLE_LOCAL_AUTH) {
try {
const user = userDb.getFirstUser();
if (!user) {
return res.status(500).json({ error: 'No user found in database (restart server after DB init)' });
}
req.user = user;
return next();
} catch (error) {
console.error('Auth bypass mode error:', error);
return res.status(500).json({ error: 'Failed to fetch user' });
}
}
const authHeader = req.headers['authorization'];
let token = authHeader && authHeader.split(' ')[1];
if (!token && req.query.token) {
token = req.query.token;
}
if (!token) {
token = extractDashboardRefererToken(req);
}
if (!token) {
return res.status(401).json({ error: 'Access denied. No token provided.' });
}
try {
const decoded = jwt.verify(token, JWT_SECRET);
const user = userDb.getUserById(decoded.userId);
if (!user) {
return res.status(401).json({ error: 'Invalid token. User not found.' });
}
if (decoded.exp && decoded.iat) {
const now = Math.floor(Date.now() / 1000);
const halfLife = (decoded.exp - decoded.iat) / 2;
if (now > decoded.iat + halfLife) {
const newToken = generateToken(user);
res.setHeader('X-Refreshed-Token', newToken);
}
}
req.user = user;
next();
} catch (error) {
console.error('Token verification error:', error);
return res.status(403).json({ error: 'Invalid token' });
}
};
const generateToken = (user) => {
return jwt.sign(
{
userId: user.id,
username: user.username
},
JWT_SECRET,
{ expiresIn: '7d' }
);
};
const authenticateWebSocket = (token) => {
if (IS_PLATFORM || DISABLE_LOCAL_AUTH) {
try {
const user = userDb.getFirstUser();
if (user) {
return { id: user.id, userId: user.id, username: user.username };
}
return null;
} catch (error) {
console.error('Platform mode WebSocket error:', error);
return null;
}
}
if (!token) {
return null;
}
try {
const decoded = jwt.verify(token, JWT_SECRET);
const user = userDb.getUserById(decoded.userId);
if (!user) {
return null;
}
return { userId: user.id, username: user.username };
} catch (error) {
console.error('WebSocket token verification error:', error);
return null;
}
};
export {
validateApiKey,
authenticateToken,
generateToken,
authenticateWebSocket,
JWT_SECRET
};