Files
rentall-app/backend/sockets/socketAuth.js
2026-01-15 16:26:53 -05:00

109 lines
2.9 KiB
JavaScript

const jwt = require("jsonwebtoken");
const { User } = require("../models");
const logger = require("../utils/logger");
const cookie = require("cookie");
/**
* Socket.io authentication middleware
* Verifies JWT token and attaches user to socket
* Tokens can be provided via:
* 1. Cookie (accessToken) - preferred for browser clients
* 2. Auth object (auth.token) - for mobile/native clients
*/
const authenticateSocket = async (socket, next) => {
try {
let token = null;
// Try to get token from cookies first (browser clients)
if (socket.handshake.headers.cookie) {
const cookies = cookie.parse(socket.handshake.headers.cookie);
token = cookies.accessToken;
}
// Auth object for mobile/native clients
if (!token && socket.handshake.auth?.token) {
token = socket.handshake.auth.token;
}
if (!token) {
logger.warn("Socket connection rejected - no token provided", {
socketId: socket.id,
address: socket.handshake.address,
});
return next(new Error("Authentication required"));
}
// Verify JWT (access tokens only)
const decoded = jwt.verify(token, process.env.JWT_ACCESS_SECRET);
const userId = decoded.id;
if (!userId) {
logger.warn("Socket connection rejected - invalid token format", {
socketId: socket.id,
});
return next(new Error("Invalid token format"));
}
// Look up user
const user = await User.findByPk(userId);
if (!user) {
logger.warn("Socket connection rejected - user not found", {
socketId: socket.id,
userId,
});
return next(new Error("User not found"));
}
// Validate JWT version (invalidate old tokens after password change)
if (decoded.jwtVersion !== user.jwtVersion) {
logger.warn("Socket connection rejected - JWT version mismatch", {
socketId: socket.id,
userId,
tokenVersion: decoded.jwtVersion,
userVersion: user.jwtVersion,
});
return next(
new Error(
"Session expired due to password change. Please log in again."
)
);
}
// Attach user to socket for use in event handlers
socket.userId = user.id;
socket.user = {
id: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
};
logger.info("Socket authenticated successfully", {
socketId: socket.id,
userId: user.id,
email: user.email,
});
next();
} catch (error) {
// Check if token is expired
if (error.name === "TokenExpiredError") {
logger.warn("Socket connection rejected - token expired", {
socketId: socket.id,
});
return next(new Error("Token expired"));
}
logger.error("Socket authentication error", {
socketId: socket.id,
error: error.message,
stack: error.stack,
});
return next(new Error("Authentication failed"));
}
};
module.exports = { authenticateSocket };