UNPKG

quilox-auth

Version:

Quilox Auth 🔑 A secure and scalable authentication and authorization middleware for Node.js and Express.js applications.

282 lines (252 loc) • 8.59 kB
/** * @file authController.js * @description This file contains the core business logic for the * Quilox Auth API. It handles user registration, login, profile management, * and user management tasks for administrators. */ // ------------------- // 1. MODULE IMPORTS // ------------------- const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); const User = require('../src/models/userModel'); // Import the Mongoose User model // ------------------- // 2. HELPER FUNCTIONS // ------------------- /** * Generates a JSON Web Token (JWT) for a user. * @param {string} id - The user's database ID. * @returns {string} - The signed JWT. */ const generateToken = (id) => { return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN, }); }; // ---------------------------------------------------- // 3. AUTHENTICATION & REGISTRATION CONTROLLER FUNCTIONS // ---------------------------------------------------- /** * @desc Registers a new user in the system. * @route POST /api/v1/auth/register * @access Public * @param {object} req - The request object from Express. * @param {object} res - The response object from Express. */ exports.register = async (req, res) => { try { const { email, password, role } = req.body; // Check if the user already exists const existingUser = await User.findOne({ email }); if (existingUser) { return res.status(400).json({ message: 'User with this email already exists' }); } // Create a new user instance const newUser = new User({ email, password, role }); await newUser.save(); // Generate a JWT for the newly created user const token = generateToken(newUser._id); // Respond with success message, user data, and the token res.status(201).json({ message: 'User registered successfully', token, user: { id: newUser._id, email: newUser.email, role: newUser.role, }, }); } catch (error) { console.error('Error during user registration:', error); res.status(500).json({ message: 'Server error during registration' }); } }; /** * @desc Authenticates a user and generates a JWT upon successful login. * @route POST /api/v1/auth/login * @access Public * @param {object} req - The request object. * @param {object} res - The response object. */ exports.login = async (req, res) => { try { const { email, password } = req.body; // Find the user by email and explicitly select the password field const user = await User.findOne({ email }).select('+password'); if (!user) { return res.status(401).json({ message: 'Invalid credentials' }); } // Compare the provided password with the stored hashed password const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { return res.status(401).json({ message: 'Invalid credentials' }); } // Generate a JWT for the authenticated user const token = generateToken(user._id); // Respond with a success message, the token, and user data (without password) res.status(200).json({ message: 'Logged in successfully', token, user: { id: user._id, email: user.email, role: user.role, }, }); } catch (error) { console.error('Error during user login:', error); res.status(500).json({ message: 'Server error during login' }); } }; // --------------------------------------------- // 4. USER PROFILE MANAGEMENT CONTROLLER FUNCTIONS // --------------------------------------------- /** * @desc Gets the profile of the authenticated user. * @route GET /api/v1/auth/profile * @access Private * @param {object} req - The request object, containing the user ID from authMiddleware. * @param {object} res - The response object. */ exports.getProfile = async (req, res) => { try { // Get the user ID from the JWT payload added by the authMiddleware const user = await User.findById(req.user.id); if (!user) { return res.status(404).json({ message: 'User not found' }); } res.status(200).json({ message: 'Profile fetched successfully', user: { id: user._id, email: user.email, role: user.role, }, }); } catch (error) { console.error('Error fetching user profile:', error); res.status(500).json({ message: 'Server error' }); } }; /** * @desc Updates the profile of the authenticated user. * @route PATCH /api/v1/auth/profile * @access Private * @param {object} req - The request object. * @param {object} res - The response object. */ exports.updateProfile = async (req, res) => { try { const userId = req.user.id; const { email, role } = req.body; // Role cannot be changed by the user themselves const updatedUser = await User.findByIdAndUpdate( userId, { email }, { new: true, runValidators: true } // Return the updated document and run schema validators ).select('-password'); if (!updatedUser) { return res.status(404).json({ message: 'User not found' }); } res.status(200).json({ message: 'Profile updated successfully', user: updatedUser, }); } catch (error) { console.error('Error updating user profile:', error); res.status(500).json({ message: 'Server error' }); } }; // ---------------------------------------------------- // 5. USER MANAGEMENT CONTROLLER FUNCTIONS (ADMIN ONLY) // ---------------------------------------------------- /** * @desc Gets a list of all users. * @route GET /api/v1/auth/users * @access Private (Admin Only) * @param {object} req - The request object. * @param {object} res - The response object. */ exports.getAllUsers = async (req, res) => { try { const users = await User.find().select('-password'); res.status(200).json({ message: 'Users fetched successfully', count: users.length, users, }); } catch (error) { console.error('Error fetching all users:', error); res.status(500).json({ message: 'Server error' }); } }; /** * @desc Gets a single user by their ID. * @route GET /api/v1/auth/users/:id * @access Private (Admin Only) * @param {object} req - The request object. * @param {object} res - The response object. */ exports.getUserById = async (req, res) => { try { const user = await User.findById(req.params.id).select('-password'); if (!user) { return res.status(404).json({ message: 'User not found' }); } res.status(200).json({ message: 'User fetched successfully', user, }); } catch (error) { console.error('Error fetching user by ID:', error); res.status(500).json({ message: 'Server error' }); } }; /** * @desc Updates a user's details by their ID. * @route PATCH /api/v1/auth/users/:id * @access Private (Admin Only) * @param {object} req - The request object. * @param {object} res - The response object. */ exports.updateUserById = async (req, res) => { try { const { email, role } = req.body; const updatedUser = await User.findByIdAndUpdate( req.params.id, { email, role }, { new: true, runValidators: true } ).select('-password'); if (!updatedUser) { return res.status(404).json({ message: 'User not found' }); } res.status(200).json({ message: 'User updated successfully', user: updatedUser, }); } catch (error) { console.error('Error updating user:', error); res.status(500).json({ message: 'Server error' }); } }; /** * @desc Deletes a user by their ID. * @route DELETE /api/v1/auth/users/:id * @access Private (Admin Only) * @param {object} req - The request object. * @param {object} res - The response object. */ exports.deleteUserById = async (req, res) => { try { const deletedUser = await User.findByIdAndDelete(req.params.id); if (!deletedUser) { return res.status(404).json({ message: 'User not found' }); } res.status(200).json({ message: 'User deleted successfully', user: deletedUser, }); } catch (error) { console.error('Error deleting user:', error); res.status(500).json({ message: 'Server error' }); } };