add changes
This commit is contained in:
139
backend/middleware/auth.middleware.js
Normal file
139
backend/middleware/auth.middleware.js
Normal file
@@ -0,0 +1,139 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const config = require('../config/config');
|
||||
const { User } = require('../models');
|
||||
|
||||
/**
|
||||
* Middleware to verify JWT token
|
||||
*/
|
||||
exports.verifyToken = async (req, res, next) => {
|
||||
try {
|
||||
// Get token from header
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'No token provided. Authorization header must be in format: Bearer <token>'
|
||||
});
|
||||
}
|
||||
|
||||
// Extract token
|
||||
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
|
||||
|
||||
// Verify token
|
||||
const decoded = jwt.verify(token, config.jwt.secret);
|
||||
|
||||
// Attach user info to request
|
||||
req.user = decoded;
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Token expired. Please login again.'
|
||||
});
|
||||
} else if (error.name === 'JsonWebTokenError') {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid token. Please login again.'
|
||||
});
|
||||
} else {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'Error verifying token',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Middleware to check if user is admin
|
||||
*/
|
||||
exports.isAdmin = async (req, res, next) => {
|
||||
try {
|
||||
// Verify token first (should be called after verifyToken)
|
||||
if (!req.user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Authentication required'
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user has admin role
|
||||
if (req.user.role !== 'admin') {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Access denied. Admin privileges required.'
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'Error checking admin privileges',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Middleware to check if user owns the resource or is admin
|
||||
*/
|
||||
exports.isOwnerOrAdmin = async (req, res, next) => {
|
||||
try {
|
||||
// Verify token first (should be called after verifyToken)
|
||||
if (!req.user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Authentication required'
|
||||
});
|
||||
}
|
||||
|
||||
const resourceUserId = req.params.userId || req.body.userId;
|
||||
|
||||
// Allow if admin or if user owns the resource
|
||||
if (req.user.role === 'admin' || req.user.userId === resourceUserId) {
|
||||
next();
|
||||
} else {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Access denied. You can only access your own resources.'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'Error checking resource ownership',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Optional auth middleware - attaches user if token present, but doesn't fail if missing
|
||||
*/
|
||||
exports.optionalAuth = async (req, res, next) => {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
const token = authHeader.substring(7);
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, config.jwt.secret);
|
||||
req.user = decoded;
|
||||
} catch (error) {
|
||||
// Token invalid or expired - continue as guest
|
||||
req.user = null;
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
// Any error - continue as guest
|
||||
next();
|
||||
}
|
||||
};
|
||||
83
backend/middleware/guest.middleware.js
Normal file
83
backend/middleware/guest.middleware.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const config = require('../config/config');
|
||||
const { GuestSession } = require('../models');
|
||||
|
||||
/**
|
||||
* Middleware to verify guest session token
|
||||
*/
|
||||
exports.verifyGuestToken = async (req, res, next) => {
|
||||
try {
|
||||
// Get token from header
|
||||
const guestToken = req.headers['x-guest-token'];
|
||||
|
||||
if (!guestToken) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'No guest token provided. X-Guest-Token header is required.'
|
||||
});
|
||||
}
|
||||
|
||||
// Verify token
|
||||
const decoded = jwt.verify(guestToken, config.jwt.secret);
|
||||
|
||||
// Check if guestId exists in payload
|
||||
if (!decoded.guestId) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid guest token. Missing guestId.'
|
||||
});
|
||||
}
|
||||
|
||||
// Verify guest session exists in database
|
||||
const guestSession = await GuestSession.findOne({
|
||||
where: { guestId: decoded.guestId }
|
||||
});
|
||||
|
||||
if (!guestSession) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Guest session not found.'
|
||||
});
|
||||
}
|
||||
|
||||
// Check if session is expired
|
||||
if (new Date() > new Date(guestSession.expiresAt)) {
|
||||
return res.status(410).json({
|
||||
success: false,
|
||||
message: 'Guest session has expired. Please start a new session.'
|
||||
});
|
||||
}
|
||||
|
||||
// Check if session was converted to user account
|
||||
if (guestSession.isConverted) {
|
||||
return res.status(410).json({
|
||||
success: false,
|
||||
message: 'Guest session has been converted to a user account. Please login with your credentials.'
|
||||
});
|
||||
}
|
||||
|
||||
// Attach guest session to request
|
||||
req.guestSession = guestSession;
|
||||
req.guestId = decoded.guestId;
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Guest token expired. Please start a new session.'
|
||||
});
|
||||
} else if (error.name === 'JsonWebTokenError') {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid guest token. Please start a new session.'
|
||||
});
|
||||
} else {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'Error verifying guest token',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
86
backend/middleware/validation.middleware.js
Normal file
86
backend/middleware/validation.middleware.js
Normal file
@@ -0,0 +1,86 @@
|
||||
const { body, validationResult } = require('express-validator');
|
||||
|
||||
/**
|
||||
* Validation middleware for user registration
|
||||
*/
|
||||
exports.validateRegistration = [
|
||||
body('username')
|
||||
.trim()
|
||||
.notEmpty()
|
||||
.withMessage('Username is required')
|
||||
.isLength({ min: 3, max: 50 })
|
||||
.withMessage('Username must be between 3 and 50 characters')
|
||||
.matches(/^[a-zA-Z0-9_]+$/)
|
||||
.withMessage('Username can only contain letters, numbers, and underscores'),
|
||||
|
||||
body('email')
|
||||
.trim()
|
||||
.notEmpty()
|
||||
.withMessage('Email is required')
|
||||
.isEmail()
|
||||
.withMessage('Please provide a valid email address')
|
||||
.normalizeEmail(),
|
||||
|
||||
body('password')
|
||||
.notEmpty()
|
||||
.withMessage('Password is required')
|
||||
.isLength({ min: 8 })
|
||||
.withMessage('Password must be at least 8 characters long')
|
||||
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
|
||||
.withMessage('Password must contain at least one uppercase letter, one lowercase letter, and one number'),
|
||||
|
||||
body('guestSessionId')
|
||||
.optional()
|
||||
.trim()
|
||||
.notEmpty()
|
||||
.withMessage('Guest session ID cannot be empty if provided'),
|
||||
|
||||
// Check for validation errors
|
||||
(req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Validation failed',
|
||||
errors: errors.array().map(err => ({
|
||||
field: err.path,
|
||||
message: err.msg
|
||||
}))
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Validation middleware for user login
|
||||
*/
|
||||
exports.validateLogin = [
|
||||
body('email')
|
||||
.trim()
|
||||
.notEmpty()
|
||||
.withMessage('Email is required')
|
||||
.isEmail()
|
||||
.withMessage('Please provide a valid email address')
|
||||
.normalizeEmail(),
|
||||
|
||||
body('password')
|
||||
.notEmpty()
|
||||
.withMessage('Password is required'),
|
||||
|
||||
// Check for validation errors
|
||||
(req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Validation failed',
|
||||
errors: errors.array().map(err => ({
|
||||
field: err.path,
|
||||
message: err.msg
|
||||
}))
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
];
|
||||
Reference in New Issue
Block a user