Implement API key authentication for secure access to mutating endpoints and WebSocket connections
This commit is contained in:
@@ -21,6 +21,33 @@ const HOST = process.env.HOST || '0.0.0.0';
|
|||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json({ limit: '5mb' }));
|
app.use(express.json({ limit: '5mb' }));
|
||||||
|
|
||||||
|
// ========== API Key Authentication ==========
|
||||||
|
|
||||||
|
const API_KEY = process.env.API_KEY || '';
|
||||||
|
|
||||||
|
// Validate bearer token from Authorization header or ?key= query param
|
||||||
|
function extractApiKey(req) {
|
||||||
|
const auth = req.headers.authorization || '';
|
||||||
|
if (auth.startsWith('Bearer ')) return auth.slice(7);
|
||||||
|
return req.query.key || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middleware: require API key on mutating endpoints
|
||||||
|
function requireAuth(req, res, next) {
|
||||||
|
if (!API_KEY) return next(); // Auth disabled when no key configured
|
||||||
|
const token = extractApiKey(req);
|
||||||
|
if (token === API_KEY) return next();
|
||||||
|
return res.status(401).json({ error: 'Unauthorized — invalid or missing API key' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply auth to all POST/PUT/DELETE routes
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
|
||||||
|
return next(); // Read-only endpoints stay open
|
||||||
|
}
|
||||||
|
return requireAuth(req, res, next);
|
||||||
|
});
|
||||||
|
|
||||||
// ========== State ==========
|
// ========== State ==========
|
||||||
const webClients = new Set();
|
const webClients = new Set();
|
||||||
const bridgeClients = new Set();
|
const bridgeClients = new Set();
|
||||||
@@ -659,11 +686,33 @@ function updateStateFromBridge(data) {
|
|||||||
|
|
||||||
// ========== WebSocket Server ==========
|
// ========== WebSocket Server ==========
|
||||||
|
|
||||||
const wss = new WebSocketServer({ server });
|
const wss = new WebSocketServer({ noServer: true });
|
||||||
|
|
||||||
console.log(`🚀 Inventory Manager Web Server starting...`);
|
console.log(`🚀 Inventory Manager Web Server starting...`);
|
||||||
console.log(`📡 HTTP Server: http://localhost:${PORT}`);
|
console.log(`📡 HTTP Server: http://localhost:${PORT}`);
|
||||||
console.log(`🔌 WebSocket Server: ws://localhost:${PORT}/ws`);
|
console.log(`🔌 WebSocket Server: ws://localhost:${PORT}/ws`);
|
||||||
|
if (API_KEY) {
|
||||||
|
console.log('🔒 API key authentication enabled');
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ No API_KEY set \u2014 authentication disabled (open access)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate WebSocket upgrades
|
||||||
|
server.on('upgrade', (req, socket, head) => {
|
||||||
|
if (API_KEY) {
|
||||||
|
// Extract key from query string: /ws?key=... or /ws/bridge?key=...
|
||||||
|
const urlObj = new URL(req.url, `http://${req.headers.host}`);
|
||||||
|
const token = urlObj.searchParams.get('key') || '';
|
||||||
|
if (token !== API_KEY) {
|
||||||
|
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||||
|
socket.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
||||||
|
wss.emit('connection', ws, req);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
wss.on('connection', (ws, req) => {
|
wss.on('connection', (ws, req) => {
|
||||||
const url = req.url || '';
|
const url = req.url || '';
|
||||||
|
|||||||
Reference in New Issue
Block a user