UNPKG

mbkauthe

Version:

MBKTechStudio's reusable authentication system for Node.js applications.

334 lines (294 loc) 12.2 kB
import { dblogin } from "./pool.js"; const mbkautheVar = JSON.parse(process.env.mbkautheVar); let pool = dblogin; const getCookieOptions = () => ({ maxAge: mbkautheVar.COOKIE_EXPIRE_TIME * 24 * 60 * 60 * 1000, domain: mbkautheVar.IS_DEPLOYED === 'true' ? `.${mbkautheVar.DOMAIN}` : undefined, secure: mbkautheVar.IS_DEPLOYED === 'true' ? 'auto' : false, sameSite: 'lax', path: '/', httpOnly: true }); async function validateSession(req, res, next) { if (!req.session.user && req.cookies.sessionId) { try { const sessionId = req.cookies.sessionId; const query = `SELECT * FROM "Users" WHERE "SessionId" = $1`; const result = await dblogin.query(query, [sessionId]); const userResult = result.rows[0]; if (result.rows.length > 0) { const user = result.rows[0]; req.session.user = { id: user.id, username: user.UserName, sessionId, }; } } catch (err) { console.error("[mbkauthe] Session validation error:", err); return res.status(500).json({ success: false, message: "Internal Server Error" }); } } if (!req.session.user) { console.log("[mbkauthe] User not authenticated"); console.log("[mbkauthe]: ", req.session.user); return res.render("Error/dError.handlebars", { layout: false, code: 401, error: "Not Logged In", message: "You Are Not Logged In. Please Log In To Continue.", pagename: "Login", page: `/mbkauthe/login?redirect=${encodeURIComponent(req.originalUrl)}`, }); } try { const { id, sessionId } = req.session.user; const query = `SELECT "SessionId", "Active", "Role", "AllowedApps" FROM "Users" WHERE "id" = $1`; const result = await dblogin.query(query, [id]); const userResult = result.rows[0]; if (result.rows.length === 0 || userResult.SessionId !== sessionId) { console.log(`[mbkauthe] Session invalidated for user "${req.session.user.username}"`); req.session.destroy(); const cookieOptions = getCookieOptions(); res.clearCookie("mbkauthe.sid", cookieOptions); res.clearCookie("sessionId", cookieOptions); res.clearCookie("username", cookieOptions); return res.render("Error/dError.handlebars", { layout: false, code: 401, error: "Session Expired", message: "Your Session Has Expired. Please Log In Again.", pagename: "Login", page: `/mbkauthe/login?redirect=${encodeURIComponent(req.originalUrl)}`, }); } if (!userResult.Active) { console.log(`[mbkauthe] Account is inactive for user "${req.session.user.username}"`); req.session.destroy(); const cookieOptions = getCookieOptions(); res.clearCookie("mbkauthe.sid", cookieOptions); res.clearCookie("sessionId", cookieOptions); res.clearCookie("username", cookieOptions); return res.render("Error/dError.handlebars", { layout: false, code: 401, error: "Account Inactive", message: "Your Account Is Inactive. Please Contact Support.", pagename: "Support", page: "https://mbktechstudio.com/Support", }); } if (userResult.Role !== "SuperAdmin") { const allowedApps = userResult.AllowedApps; if (!allowedApps || !allowedApps.some(app => app.toLowerCase() === mbkautheVar.APP_NAME.toLowerCase())) { console.warn(`[mbkauthe] User \"${req.session.user.username}\" is not authorized to use the application \"${mbkautheVar.APP_NAME}\"`); req.session.destroy(); const cookieOptions = getCookieOptions(); res.clearCookie("mbkauthe.sid", cookieOptions); res.clearCookie("sessionId", cookieOptions); res.clearCookie("username", cookieOptions); return res.render("Error/dError.handlebars", { layout: false, code: 401, error: "Unauthorized", message: `You Are Not Authorized To Use The Application \"${mbkautheVar.APP_NAME}\"`, pagename: "Home", page: `/${mbkautheVar.loginRedirectURL}` }); } } next(); } catch (err) { console.error("[mbkauthe] Session validation error:", err); res.status(500).json({ success: false, message: "Internal Server Error" }); } } const checkRolePermission = (requiredRole) => { return async (req, res, next) => { try { if (!req.session || !req.session.user || !req.session.user.id) { console.log("[mbkauthe] User not authenticated"); console.log("[mbkauthe]: ", req.session); return res.render("Error/dError.handlebars", { layout: false, code: 401, error: "Not Logged In", message: "You Are Not Logged In. Please Log In To Continue.", pagename: "Login", page: `/mbkauthe/login?redirect=${encodeURIComponent(req.originalUrl)}`, }); } if (requiredRole === "Any" || requiredRole === "any") { return next(); } const userId = req.session.user.id; const query = `SELECT "Role" FROM "Users" WHERE "id" = $1`; const result = await dblogin.query(query, [userId]); if (result.rows.length === 0) { return res.status(401).json({ success: false, message: "User not found" }); } const userRole = result.rows[0].Role; if (userRole !== requiredRole) { return res.render("Error/dError.handlebars", { layout: false, code: 403, error: "Access Denied", message: `You do not have permission to access this resource. Required role: ${requiredRole}`, pagename: "Home", page: `/${mbkautheVar.loginRedirectURL}` }); } next(); } catch (err) { console.error("[mbkauthe] Permission check error:", err); res.status(500).json({ success: false, message: "Internal Server Error" }); } }; }; const validateSessionAndRole = (requiredRole) => { return async (req, res, next) => { await validateSession(req, res, async () => { await checkRolePermission(requiredRole)(req, res, next); }); }; }; async function getUserData(UserName, parameters) { try { if (!parameters || parameters.length === 0) { throw new Error("Parameters are required to fetch user data"); } const userFields = [ "Password", "UserName", "Role", "Active", "GuestRole", "HaveMailAccount", "AllowedApps", ]; const profileFields = [ "FullName", "email", "Image", "ProjectLinks", "SocialAccounts", "Bio", "Positions", ]; let userParameters = []; let profileParameters = []; if (parameters === "profiledata") { userParameters = userFields.filter(field => field !== "Password"); profileParameters = profileFields; } else { userParameters = userFields.filter(field => parameters.includes(field)); profileParameters = profileFields.filter(field => parameters.includes(field)); } let userResult = {}; if (userParameters.length > 0) { const userQuery = `SELECT ${userParameters.map(field => `"${field}"`).join(", ")} FROM "Users" WHERE "UserName" = $1`; const userQueryResult = await dblogin.query(userQuery, [UserName]); if (userQueryResult.rows.length === 0) return { error: "User not found" }; userResult = userQueryResult.rows[0]; } let profileResult = {}; if (profileParameters.length > 0) { const profileQuery = `SELECT ${profileParameters.map(field => `"${field}"`).join(", ")} FROM profiledata WHERE "UserName" = $1`; const profileQueryResult = await dblogin.query(profileQuery, [UserName]); if (profileQueryResult.rows.length === 0) return { error: "Profile data not found" }; profileResult = profileQueryResult.rows[0]; } const combinedResult = { ...userResult, ...profileResult }; return combinedResult; } catch (err) { console.error("[mbkauthe] Error fetching user data:", err.message); throw err; } } const authenticate = (authentication) => { return (req, res, next) => { const token = req.headers["authorization"]; console.log(`[mbkauthe] Received token: ${token}`); if (token === authentication) { console.log("[mbkauthe] Authentication successful"); next(); } else { console.log("[mbkauthe] Authentication failed"); res.status(401).send("Unauthorized"); } }; }; const authapi = (requiredRole = []) => { return (req, res, next) => { const token = req.headers["authorization"]; if (typeof token === 'string') { console.log("[mbkauthe] [authapi] Received request with token:", token[0] + token[1] + token[2], ".....", token[63]); } else { console.log("[mbkauthe] [authapi] Token is not a valid string:", token); } if (!token) { console.log("[mbkauthe] [authapi] No token provided in the request headers"); return res.status(401).json({ success: false, message: "Authorization token is required" }); } console.log("[mbkauthe] [authapi] Querying database to validate token"); const tokenQuery = 'SELECT * FROM "UserAuthApiKey" WHERE "key" = $1'; pool.query(tokenQuery, [token], (err, result) => { if (err) { console.error("[mbkauthe] [authapi] Database query error while validating token:", err); return res.status(500).json({ success: false, message: "Internal Server Error" }); } if (result.rows.length === 0) { console.log("[mbkauthe] [authapi] Invalid token provided:", token); return res.status(401).json({ success: false, message: "The AuthApiToken Is Invalid" }); } const username = result.rows[0].username; console.log("[mbkauthe] [authapi] Token is valid. Associated username:", username); console.log("[mbkauthe] [authapi] Querying database to validate user and role"); const userQuery = ` SELECT id, "UserName", "Active", "Role" FROM "Users" WHERE "UserName" = $1 AND "Active" = true `; pool.query(userQuery, [username], (err, userResult) => { if (err) { console.error("[mbkauthe] [authapi] Database query error while validating user:", err); return res.status(500).json({ success: false, message: "Internal Server Error" }); } if (userResult.rows.length === 0) { console.log("[mbkauthe] [authapi] User does not exist or is not active. Username:", username); return res.status(401).json({ success: false, message: "User does not exist or is not active", }); } if (username === "demo") { console.log("[mbkauthe] [authapi] Demo user attempted to access an endpoint. Access denied."); return res.status(401).json({ success: false, message: "Demo user is not allowed to access endpoints", }); } const user = userResult.rows[0]; console.log("[mbkauthe] [authapi] User is valid. User details:", user); // Check if role is required and if user has it if ((requiredRole && user.Role !== requiredRole) && user.Role !== "SuperAdmin") { console.log(`[mbkauthe] [authapi] User does not have the required role. Required: ${requiredRole}, User's role: ${user.Role}`); return res.status(403).json({ success: false, message: `Access denied. Required role: ${requiredRole}`, }); } console.log("[mbkauthe] [authapi] User has the required role or no specific role is required. Proceeding to next middleware."); req.user = { username: user.UserName, role: user.Role, // Add other user properties you might need }; console.log("[mbkauthe] [authapi] Token and user validation successful. Passing control to next middleware."); next(); }); }); }; }; export { validateSession, checkRolePermission, validateSessionAndRole, getUserData, authenticate, authapi };