diff --git a/backend/src/middleware/jwtAuth.js b/backend/src/middleware/jwtAuth.js new file mode 100644 index 0000000..acb7937 --- /dev/null +++ b/backend/src/middleware/jwtAuth.js @@ -0,0 +1,34 @@ +const jwt = require('jsonwebtoken'); + +/** + * JWT authentication middleware. + * Validates the Authorization: Bearer header using the secret + * from the JWT_SECRET environment variable. + * On success attaches the decoded payload (wallet, role, exp, etc.) to req.user. + */ +function jwtAuthMiddleware(req, res, next) { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'Missing Authorization header' }); + } + + const token = authHeader.slice(7).trim(); + const secret = process.env.JWT_SECRET; + if (!secret) { + // Configuration error – treat as server error but do not expose secret + return res.status(500).json({ error: 'JWT secret not configured' }); + } + + try { + const decoded = jwt.verify(token, secret); + // Attach only relevant fields (wallet, role, exp) to req.user + const { wallet, role, exp } = decoded; + req.user = { wallet, role, exp }; + next(); + } catch (err) { + // jwt.verify throws for invalid signature, expired token, etc. + return res.status(401).json({ error: 'Invalid or expired token' }); + } +} + +module.exports = jwtAuthMiddleware; diff --git a/backend/src/routes/vaccination.js b/backend/src/routes/vaccination.js index 9fc4dba..9f1e2c6 100644 --- a/backend/src/routes/vaccination.js +++ b/backend/src/routes/vaccination.js @@ -125,6 +125,11 @@ router.post( result: 'success', meta: { token_id: result.tokenId, vaccine_name, date_administered, dose_number, dose_series }, }); + // Invalidate verification cache for this wallet + const verifyRouter = require('../routes/verify'); + if (verifyRouter.verifyCache) { + verifyRouter.verifyCache.delete(patient_address); + } res.json({ success: true, diff --git a/backend/src/routes/verify.js b/backend/src/routes/verify.js index c69df6c..f605d04 100644 --- a/backend/src/routes/verify.js +++ b/backend/src/routes/verify.js @@ -12,6 +12,7 @@ const router = express.Router(); const verifyCache = new Map(); const CACHE_TTL = 60 * 1000; // 60 seconds +const MAX_CACHE_SIZE = 1000; // maximum entries to bound memory /** * Try JWT first; if no Authorization header, fall through to API key auth. @@ -108,6 +109,11 @@ router.get( records, timestamp: now }); + // Evict oldest entry if cache exceeds max size + if (verifyCache.size > MAX_CACHE_SIZE) { + const oldestKey = verifyCache.keys().next().value; + verifyCache.delete(oldestKey); + } audit({ actor: ip, @@ -217,6 +223,11 @@ router.get( records, timestamp: now }); + // Evict oldest entry if cache exceeds max size + if (verifyCache.size > MAX_CACHE_SIZE) { + const oldestKey = verifyCache.keys().next().value; + verifyCache.delete(oldestKey); + } audit({ actor,