Middleware
Middleware files live in apps/backend/src/middleware/.
auth.middleware.js
Exports three request guard functions used to protect routes.
authCheck — Authenticated User Guard
Validates that the request has a valid JWT and that the session is still active (not revoked).
Used on: all /api/v1/user/*, /api/v1/unit/*, /api/v1/device/*, /api/v1/report/*, /api/v1/recipient/* routes.
Steps:
- Reads
userAccessTokenfromreq.cookies - Verifies JWT signature and expiry using
process.env.JWT_SECRET - Looks up the token in the
user_tokenscollection (server-side revocation check) - Looks up the user by
decoded._idin theuserscollection - Sets
req.user = userandreq.token = token - Calls
next()
Failure response: 401 { success: false, message: 'Please authenticate' }
adminAuthCheck — Admin-Only Guard
Performs the same full session validation as authCheck, then additionally checks that the user has type === 'admin'.
Used on: all /api/v1/admin/* routes.
Failure responses:
401 { success: false, message: 'Please authenticate' }— invalid/missing token401 { success: false, message: 'Please authenticate' }— if user is not admin (same response, no leakage)
deviceAuthCheck — IoT Device Token Guard
Validates IoT hardware requests using a static shared secret in the Authorization header.
Used on: POST /api/v1/devices/sensor-data only.
Steps:
- Reads
req.headers['authorization'] - Compares against the hardcoded token string
- Sets
req.user = {} - Calls
next()
Failure response: 401 { success: false, message: 'Please authenticate' }
Security
The deviceAuthCheck uses a hardcoded static token. This value should be moved to an environment variable (DEVICE_AUTH_TOKEN) to allow rotation without code changes.
Middleware Usage in Routes
// user route — any logged-in user
router.get('/profile', authCheck, userController.getProfile)
// admin route — admin only
router.get('/', adminAuthCheck, adminUserController.listUsers)
// IoT device route — hardware token
router.post('/', deviceAuthCheck, deviceSensorDataController.saveSensorData)Error Handling
There is a global error handler pattern defined in src/helpers/response.helper.js. Controllers are expected to catch errors themselves and use the response helpers:
const getProfile = async (req, res) => {
try {
const result = await userBusiness.getProfile(req.user._id)
successResponse({ res, data: result })
} catch (error) {
console.error(error)
internalServerError({ res })
}
}An Express catch-all error handler (app.use((err, req, res, next) => {...})) should be added after all route mounts to catch unhandled errors.
