From 37c92c13c398e9292524f69be8e043a1ebfd00ee Mon Sep 17 00:00:00 2001 From: sabarinathan Date: Fri, 29 Aug 2025 17:42:55 +0530 Subject: [PATCH] Enhance socket authentication with access and refresh token handling --- src/socket-server/manager/manager.ts | 105 ++++++++++++++++++--------- 1 file changed, 71 insertions(+), 34 deletions(-) diff --git a/src/socket-server/manager/manager.ts b/src/socket-server/manager/manager.ts index cb0d53d..46f5d2f 100644 --- a/src/socket-server/manager/manager.ts +++ b/src/socket-server/manager/manager.ts @@ -1,6 +1,7 @@ import { Server, Socket } from "socket.io"; import jwt from "jsonwebtoken"; import { eventHandlerMap } from "../events/eventHandaler"; +import { existingUserData } from "../../shared/services/AuthService"; interface UserPayload { userId: string; @@ -16,7 +17,7 @@ interface UserSocketInfo { const connectedUsersByOrg: { [organization: string]: UserSocketInfo[] } = {}; -export const SocketServer = (io: Server) => { +export const SocketServer = async (io: Server) => { // ✅ Declare all namespaces here const namespaceNames = ["/edge", "/project", "/graph"]; @@ -26,55 +27,91 @@ export const SocketServer = (io: Server) => { // ✅ Attach common handler to each namespace namespaceNames.forEach((nspName) => { const namespace = io.of(nspName); - + namespace.use(async (socket: Socket, next) => { + try { + const accessToken = socket.handshake.auth.accessToken as string; + const refreshToken = socket.handshake.auth.refreshToken as string; + if (!accessToken) return next(new Error("No access token provided")); + const jwt_secret = process.env.JWT_SECRET as string; + try { + const decoded = jwt.verify(accessToken, jwt_secret) as UserPayload; - // namespace.use((socket: Socket, next) => { - // try { - // socket.handshake.auth - // console.log('socket.handshake.auth: ', socket.handshake.auth); - // const token = socket.handshake.auth.token as string; - // if (!token) return next(new Error("No token provided")); + const mailExistance = await existingUserData(decoded.email, decoded.organization); + if (!mailExistance) { + return next(new Error("Unauthorized user - not in system")); + } - // const jwt_secret = process.env.JWT_SECRET as string; - // const decoded = jwt.verify(token, jwt_secret) as UserPayload; + (socket as any).user = decoded; + return next(); - // (socket as any).user = decoded; - // next(); - // } catch (err) { - // next(new Error("Authentication failed")); - // } - // }); + } catch (err: any) { + if (err.name === "TokenExpiredError") { + if (!refreshToken) { + return next(new Error("Token expired and no refresh token provided")); + } + + try { + const refreshSecret = process.env.REFRESH_JWT_SECRET as string; + const decodedRefresh = jwt.verify(refreshToken, refreshSecret) as UserPayload; + const mailExistance = await existingUserData(decodedRefresh.email, decodedRefresh.organization); + + if (!mailExistance) { + return next(new Error("Unauthorized user - not in system")); + } + + // Generate new access token + const newAccessToken = jwt.sign( + { email: decodedRefresh.email, organization: decodedRefresh.organization }, + jwt_secret, + { expiresIn: "3h" } // adjust expiry + ); + + socket.emit("newAccessToken", newAccessToken); + + (socket as any).user = decodedRefresh; + return next(); + } catch (refreshErr) { + return next(new Error("Refresh token expired or invalid")); + } + } + + return next(new Error("Invalid token")); + } + } catch (err) { + return next(new Error("Authentication error")); + } + }); namespace.on("connection", (socket: Socket) => { - console.log(`✅ Connected to namespace ${nspName}: ${socket.id}`); - // const user = (socket as any).user as UserPayload; - // const { organization, userId } = user; + const user = (socket as any).user as UserPayload; + const { organization, userId } = user; - // if (!onlineUsers[organization]) onlineUsers[organization] = new Set(); - // onlineUsers[organization].add(socket.id); + if (!onlineUsers[organization]) onlineUsers[organization] = new Set(); + onlineUsers[organization].add(socket.id); - // if (!connectedUsersByOrg[organization]) connectedUsersByOrg[organization] = []; - // connectedUsersByOrg[organization].push({ socketId: socket.id, userId, organization }); + if (!connectedUsersByOrg[organization]) connectedUsersByOrg[organization] = []; + connectedUsersByOrg[organization].push({ socketId: socket.id, userId, organization }); + console.log(`✅ Connected to namespace ${nspName}: ${socket.id}`); - // 🎯 Common event handler + // Common event handler socket.onAny((event: string, data: any, callback: any) => { const handler = eventHandlerMap[event]; if (handler) { handler(event, socket, io, data, connectedUsersByOrg, callback); } else { - console.warn(`⚠️ No handler found for event: ${event}`); + console.warn(` No handler found for event: ${event}`); } }); - // socket.on("disconnect", () => { - // onlineUsers[organization]?.delete(socket.id); - // if (onlineUsers[organization]?.size === 0) { - // delete onlineUsers[organization]; - // } - // connectedUsersByOrg[organization] = connectedUsersByOrg[organization]?.filter( - // (u) => u.socketId !== socket.id - // ); - // }); + socket.on("disconnect", () => { + onlineUsers[organization]?.delete(socket.id); + if (onlineUsers[organization]?.size === 0) { + delete onlineUsers[organization]; + } + connectedUsersByOrg[organization] = connectedUsersByOrg[organization]?.filter( + (u) => u.socketId !== socket.id + ); + }); }); });