mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
86 lines
3.4 KiB
JavaScript
86 lines
3.4 KiB
JavaScript
/***********************************************************************************************************************************************************************
|
|
* File Name: auth.middleware.js
|
|
* Type of Program: Middleware
|
|
* Description: JWT authentication guard.
|
|
* Reads the Bearer token from the Authorization header,
|
|
* verifies it, attaches decoded payload to req.user,
|
|
* and verifies the session is still active in the DB.
|
|
* Author: rgrgogu
|
|
* Date Created: Oct. 6, 2025
|
|
***********************************************************************************************************************************************************************
|
|
* HOW TO USE:
|
|
* const { authenticate } = require('../middleware/auth.middleware');
|
|
* router.get('/profile', authenticate, handler);
|
|
***********************************************************************************************************************************************************************/
|
|
const { verifyAccessToken } = require('../utils/token.util');
|
|
const mdl_Users = require('../models/users/users.mdl');
|
|
const R = require('../utils/response.util');
|
|
|
|
/**
|
|
* Validates JWT and loads user from DB.
|
|
* Attaches the full user record to req.user.
|
|
*/
|
|
const authenticate = async (req, res, next) => {
|
|
try {
|
|
const authHeader = req.headers.authorization;
|
|
if (!authHeader?.startsWith('Bearer '))
|
|
return R.error(res, 'No token provided.', 401);
|
|
|
|
const token = authHeader.split(' ')[1];
|
|
const decoded = verifyAccessToken(token);
|
|
|
|
const user = await mdl_Users.findByPk(decoded.user_id, {
|
|
attributes: { exclude: ['password', 'otp_code', 'otp_expires_at'] },
|
|
});
|
|
|
|
if (!user) return R.error(res, 'User not found.', 401);
|
|
if (!user.is_active) return R.error(res, 'Account is deactivated.', 403);
|
|
|
|
req.user = user;
|
|
next();
|
|
} catch (err) {
|
|
if (err.name === 'TokenExpiredError')
|
|
return R.error(res, 'Token expired. Please log in again.', 401);
|
|
return R.error(res, 'Invalid token.', 401);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Soft authentication for endpoints that are useful both authenticated and unauthenticated.
|
|
*
|
|
* Behaviour:
|
|
* - No Authorization header → guest mode (req.user = null, continues)
|
|
* - Valid token → authenticated (req.user = user, continues)
|
|
* - Present but invalid/expired token → 401 (a token was attempted; reject it)
|
|
*
|
|
* The distinction between "no token" and "bad token" matters:
|
|
* silently ignoring a malformed token would allow it to be used as a scapegoat
|
|
* to probe the endpoint without being flagged as unauthenticated.
|
|
*/
|
|
const softAuthenticate = async (req, res, next) => {
|
|
const authHeader = req.headers.authorization;
|
|
|
|
// No token at all → guest mode
|
|
if (!authHeader?.startsWith('Bearer ')) {
|
|
req.user = null;
|
|
return next();
|
|
}
|
|
|
|
// Token present → validate it fully (same rules as authenticate)
|
|
try {
|
|
const token = authHeader.split(' ')[1];
|
|
const decoded = verifyAccessToken(token);
|
|
const user = await mdl_Users.findByPk(decoded.user_id, {
|
|
attributes: { exclude: ['password', 'otp_code', 'otp_expires_at'] },
|
|
});
|
|
|
|
req.user = (user && user.is_active) ? user : null;
|
|
next();
|
|
} catch (err) {
|
|
if (err.name === 'TokenExpiredError')
|
|
return R.error(res, 'Token expired. Please log in again.', 401);
|
|
return R.error(res, 'Invalid token.', 401);
|
|
}
|
|
};
|
|
|
|
module.exports = { authenticate, softAuthenticate }; |