UNPKG

4bnode

Version:

4bnode is a CLI-powered backend development platform with a built-in visual dashboard to generate, manage, and test Node.js/Express APIs faster.

50 lines (44 loc) 1.81 kB
// API key authentication. Named keys are managed by the 4bnode dashboard and // stored in src/api-keys.json. Send a key in the `x-api-key` request header: // import apiKey from './src/middleware/apiKey.js'; // app.use('/api/private', apiKey); import fs from 'fs'; import path from 'path'; import crypto from 'crypto'; import { fileURLToPath } from 'url'; const KEYS_FILE = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'api-keys.json'); let _cache = { mtimeMs: -1, keys: [] }; // Read the keys file fresh when it changes (mtime-cached), so keys generated or // revoked in the dashboard take effect immediately — no restart needed. function loadKeys() { try { const { mtimeMs } = fs.statSync(KEYS_FILE); if (mtimeMs !== _cache.mtimeMs) { const list = JSON.parse(fs.readFileSync(KEYS_FILE, 'utf8')); _cache = { mtimeMs, keys: (Array.isArray(list) ? list : []).map((k) => k && k.key).filter(Boolean) }; } } catch { _cache = { mtimeMs: -1, keys: [] }; } return _cache.keys; } // Constant-time membership test: hash both sides to a fixed length so neither the // match result nor the key length leaks through string-comparison timing. function keyMatches(provided, valid) { const p = crypto.createHash('sha256').update(String(provided)).digest(); let ok = false; for (const k of valid) { const h = crypto.createHash('sha256').update(String(k)).digest(); if (crypto.timingSafeEqual(p, h)) ok = true; // no early return — keep timing flat } return ok; } const apiKey = (req, res, next) => { const provided = req.header('x-api-key'); const valid = loadKeys(); if (!provided || !keyMatches(provided, valid)) { return res.status(401).json({ message: 'Invalid or missing API key' }); } next(); }; export default apiKey;