39647-vm/backend/controllers/authController.js
tornikegerantia 171fd243de Implement a fully functional user login and registration system
Refactor the authentication system to separate login and registration forms, implement password hashing, manage JWT tokens, and enable user data persistence in `backend/data/users.json`.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 375ec6d3-d5af-4f82-ab81-5c60fd4a86a3
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 8d427c3d-aa60-488b-82c4-6cef148ba5d7
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/147e665c-8c0d-48ec-b0ad-fdc89cd4460f/375ec6d3-d5af-4f82-ab81-5c60fd4a86a3/e238nM8
Replit-Helium-Checkpoint-Created: true
2026-04-15 00:19:58 +00:00

187 lines
4.9 KiB
JavaScript

const jwt = require('jsonwebtoken');
const User = require('../models/userModel');
const { validateEmail, validatePassword } = require('../middleware/validationMiddleware');
// Generate JWT Token
const generateToken = (id) => {
return jwt.sign({ id }, process.env.JWT_SECRET || 'your_secret_key', {
expiresIn: process.env.JWT_EXPIRE || '7d',
});
};
// @desc Register user
// @route POST /api/auth/register
// @access Public
exports.register = async (req, res) => {
try {
const { firstName, lastName, email, password, passwordConfirm } = req.body;
const normalizedEmail = email ? email.toLowerCase().trim() : '';
// Validation
if (!firstName || !lastName || !email || !password || !passwordConfirm) {
return res.status(400).json({ message: 'All fields are required' });
}
if (!validateEmail(normalizedEmail)) {
return res.status(400).json({ message: 'Invalid email format' });
}
if (!validatePassword(password)) {
return res.status(400).json({
message: 'Password must be at least 6 characters',
});
}
if (password !== passwordConfirm) {
return res.status(400).json({ message: 'Passwords do not match' });
}
// Check if user already exists
let user = await User.findOne({ email: normalizedEmail });
if (user) {
return res.status(400).json({ message: 'Email already in use' });
}
// Create user
user = await User.create({
firstName,
lastName,
email: normalizedEmail,
password,
});
// Create token
const token = generateToken(user._id);
res.status(201).json({
success: true,
message: 'User registered successfully',
token,
user: user.getPublicData(),
});
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// @desc Login user
// @route POST /api/auth/login
// @access Public
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
const normalizedEmail = email ? email.toLowerCase().trim() : '';
// Validation
if (!email || !password) {
return res.status(400).json({ message: 'Email and password are required' });
}
if (!validateEmail(normalizedEmail)) {
return res.status(400).json({ message: 'Invalid email format' });
}
const user = await User.findOne({ email: normalizedEmail });
if (!user) {
return res.status(401).json({ message: 'Invalid email or password' });
}
// Check if password matches
const isPasswordMatch = await user.matchPassword(password);
if (!isPasswordMatch) {
return res.status(401).json({ message: 'Invalid email or password' });
}
// Check if user account is active
if (!user.isActive) {
return res.status(403).json({ message: 'Your account has been deactivated' });
}
// Update last login
user.lastLogin = new Date();
await user.save();
// Create token
const token = generateToken(user._id);
res.status(200).json({
success: true,
message: 'Login successful',
token,
user: user.getPublicData(),
});
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// @desc Logout user (client-side)
// @route POST /api/auth/logout
// @access Private
exports.logout = async (req, res) => {
try {
// Token removal is handled on client-side
// This endpoint can be used for server-side session cleanup if needed
res.status(200).json({
success: true,
message: 'Logout successful',
});
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// @desc Refresh token
// @route POST /api/auth/refresh-token
// @access Private
exports.refreshToken = async (req, res) => {
try {
const { token } = req.body;
if (!token) {
return res.status(400).json({ message: 'Token is required' });
}
// Verify token
let decoded;
try {
decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_secret_key');
} catch (error) {
return res.status(401).json({ message: 'Invalid or expired token' });
}
// Get user
const user = await User.findById(decoded.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
// Generate new token
const newToken = generateToken(user._id);
res.status(200).json({
success: true,
message: 'Token refreshed successfully',
token: newToken,
});
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// @desc Get current logged in user
// @route GET /api/auth/me
// @access Private
exports.getMe = async (req, res) => {
try {
const user = await User.findById(req.user._id);
res.status(200).json({
success: true,
user: user.getPublicData(),
});
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};