real time messaging
This commit is contained in:
111
backend/sockets/socketAuth.js
Normal file
111
backend/sockets/socketAuth.js
Normal file
@@ -0,0 +1,111 @@
|
||||
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. Query parameter (token) - fallback for mobile/other 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;
|
||||
}
|
||||
|
||||
// Fallback to query parameter (mobile/other clients)
|
||||
if (!token && socket.handshake.auth?.token) {
|
||||
token = socket.handshake.auth.token;
|
||||
}
|
||||
|
||||
// Fallback to legacy query parameter
|
||||
if (!token && socket.handshake.query?.token) {
|
||||
token = socket.handshake.query.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
|
||||
const decoded = jwt.verify(token, process.env.JWT_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 };
|
||||
Reference in New Issue
Block a user