Source

API/src/middlewares/auth.js

/**
 * @fileoverview Basic authentication middleware.
 * 
 * @category Backend API
 * @subcategory Middlewares
 * 
 * @module Basic Authentication
 * 
 * @description This module contains the basic authentication middleware.
 * 
 * @requires ../utils/errors
 * @requires jsonwebtoken
 * @requires ../models/token.models
 * @requires ../utils/token
 * @requires ../utils/config
 */


const { UnauthenticatedError } = require('../utils/errors');
const jwt = require('jsonwebtoken');
const { BlacklistedToken } = require('../models/token.models');
const { getAuthTokens, getRequiredConfigVars } = require('../utils/token');

const config = require('../utils/config');

/**
 * Middleware to check if the request has a valid authorization header
 * and if the token is valid.
 * 
 * @description This middleware checks if the incoming request 
 * has a valid authorization header with a JWT token,
 * and verifies the token to ensure that it's valid. 
 * It also checks if the token has been blacklisted or revoked,
 * and ensures that the user's account is active before allowing 
 * the request to proceed to the next middleware.The middleware 
 * function returns a Promise that resolves if the 
 * authorization header and token are valid and the user's account 
 * is active, or rejects with an UnauthenticatedError if 
 * any of these conditions are not met.
 * 
 * <br>
 * <br>
 *
 * If a `token_type` parameter is specified, the middleware 
 * function will use the JWT secret for the specified token
 * type to verify the token. 
 * If the `token_type` parameter is not specified, the function 
 * will use the default access token secret defined in the configuration file.
 *
 * <br>
 * <br>
 * 
 * If the incoming request is a GET request to the `/authtoken` endpoint, 
 * the middleware will return a new access token for the user, 
 * without performing any authorization checks.
 * 
 * @param {string} token_type (optional) - specifies the type of token to be used
 * @returns {Function} Express middleware function
 *
 * @throws {UnauthenticatedError} If the request does not have a valid authorization header.
 * @throws {UnauthenticatedError} If the token is invalid.
 * @throws {UnauthenticatedError} If the token has been blacklisted.
 * @throws {UnauthenticatedError} If the user's account is not active.
 *
 * @example
 * // Use basicAuth middleware to authenticate incoming requests
 * app.get('/api/protected', basicAuth(), (req, res) => {
 *   // do something with req.user and req.token
 *   res.send('Hello World');
 * });
 *
 * @example
 * // Use basicAuth middleware to authenticate incoming requests for a specific token type
 * app.get('/api/protected', basicAuth('verifycation'), (req, res) => {
 *   // here the token type is specified as verification
 *   // do something with req.user and req.token
 *   res.send('Hello World');
 * });
 */
const basicAuth = function (token_type = null) {
    return async (req, res, next) => {
        // Check if the request has a valid authorization header
        const authHeader = req.headers.authorization;
        if (!authHeader || !authHeader.startsWith('Bearer ')) {
            if (token_type == 'optional') return next();
            return next(new UnauthenticatedError('Invalid authorization header'));
        }

        // if (token_type == 'optional') return next();
        token_type = token_type == 'optional' ? null : token_type;

        // Use the default access token secret if token_type is not specified or is optional
        // Otherwise use the secret for the specified token type
        token_type = !token_type ? "access" : token_type;

        let { secret } = getRequiredConfigVars(token_type)

        // Verify the token
        const jwtToken = authHeader.split(' ')[1]; //console.log(jwtToken)
        const payload = jwt.verify(jwtToken, secret);
        req.user = payload;
        req.token = jwtToken;

        // Check if access token has been blacklisted
        const blacklisted = await BlacklistedToken.findOne({ token: jwtToken });
        if (blacklisted) {
            return next(new UnauthenticatedError('JWT token expired'));
        }

        // To get new access token
        if (req.method == 'GET' && req.path == '/authtoken') {
            const new_access_token = (await getAuthTokens(payload.id))
                .access_token;

            return res
                .status(200)
                .send({ message: 'success', access_token: new_access_token });
        }

        if (!req.user.status.isActive && !token_type) {
            return next(
                new UnauthenticatedError(
                    'Unauthorized access, users account is not active'
                )
            );
        }

        // If all is well, proceed to the next middleware
        next();
    };
};

module.exports = {
    basicAuth,
};