Skip to content

Commit 5873272

Browse files
committed
single session
1 parent 2510f06 commit 5873272

File tree

3 files changed

+51
-11
lines changed

3 files changed

+51
-11
lines changed

backend/src/services/websocket.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export class WebSocketService {
3636
}
3737

3838
private async verifyToken(
39-
token: string,
39+
token: string
4040
): Promise<{ id: string; provider: AuthProvider }> {
4141
return new Promise((resolve, reject) => {
4242
jwt.verify(token, process.env.JWT_SECRET!, (err, decoded) => {
@@ -86,6 +86,29 @@ export class WebSocketService {
8686
if (!token) throw new Error("No token provided");
8787

8888
const user = await this.verifyToken(token);
89+
90+
const existingConnection = this.connections.get(user.id);
91+
if (
92+
existingConnection &&
93+
existingConnection.readyState === WebSocket.OPEN
94+
) {
95+
logger.info(
96+
`Existing connection for ${user.id} found. Closing the old one.`
97+
);
98+
existingConnection.send(
99+
JSON.stringify({
100+
type: "FORCE_DISCONNECT",
101+
payload: {
102+
message: "You have signed in from another device.",
103+
},
104+
})
105+
);
106+
existingConnection.close(
107+
4000,
108+
"New connection established by the same user."
109+
);
110+
}
111+
89112
ws.playerId = user.id;
90113
this.connections.set(user.id, ws);
91114

@@ -110,7 +133,7 @@ export class WebSocketService {
110133

111134
private async handleMessage(
112135
ws: AuthenticatedWebSocket,
113-
data: WebSocket.RawData,
136+
data: WebSocket.RawData
114137
): Promise<void> {
115138
try {
116139
const now = Date.now();
@@ -154,22 +177,22 @@ export class WebSocketService {
154177
await this.roomService.createRoom(
155178
payload.type,
156179
ws.playerId,
157-
payload.inviteCode,
180+
payload.inviteCode
158181
);
159182
break;
160183

161184
case "JOIN_ROOM":
162185
await this.roomService.joinRoom(
163186
payload.roomId,
164187
ws.playerId,
165-
payload.inviteCode,
188+
payload.inviteCode
166189
);
167190
break;
168191

169192
case "REQUEST_REJOIN":
170193
await this.roomService.handleRequestRejoin(
171194
ws.playerId,
172-
payload.gameId,
195+
payload.gameId
173196
);
174197
break;
175198

@@ -193,15 +216,15 @@ export class WebSocketService {
193216
await this.gameService.makeMove(
194217
payload.gameId,
195218
ws.playerId,
196-
payload.move,
219+
payload.move
197220
);
198221
break;
199222

200223
case "GET_LEGAL_MOVES":
201224
await this.gameService.getLegalMoves(
202225
payload.gameId,
203226
ws.playerId,
204-
payload.square,
227+
payload.square
205228
);
206229
break;
207230

@@ -225,7 +248,7 @@ export class WebSocketService {
225248
await this.chatService.sendChatMessage(
226249
payload.gameId,
227250
ws.playerId,
228-
payload.message,
251+
payload.message
229252
);
230253
break;
231254

@@ -268,7 +291,7 @@ export class WebSocketService {
268291
public broadcastToGame(
269292
game: Game,
270293
messageType?: string,
271-
payload?: any,
294+
payload?: any
272295
): void {
273296
const typeToSend = messageType || "GAME_UPDATED";
274297

frontend/src/store/websocket.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,13 @@ export const useWebSocketStore = create<WebSocketState>((set, get) => ({
108108

109109
useAuthStore.getState().setStatus(UserStatus.OFFLINE);
110110

111-
if (event.code === WS_CLOSE_CODES.AUTH_FAILED) {
111+
if (event.code === WS_CLOSE_CODES.FORCE_DISCONNECT) {
112+
set({
113+
error: "Signed in from anohter device",
114+
});
115+
useAuthStore.getState().clearAuth();
116+
return;
117+
} else if (event.code === WS_CLOSE_CODES.AUTH_FAILED) {
112118
set({ error: "Authentication failed" });
113119
toast.error("Authentication failed. Please log in again.");
114120
useAuthStore.getState().clearAuth();
@@ -293,6 +299,10 @@ const handleServerMessage = (message: ServerMessage) => {
293299
handleRoomMessage(message);
294300
break;
295301

302+
case "FORCE_DISCONNECT":
303+
toast.info(message.payload.message);
304+
break;
305+
296306
default:
297307
console.warn("Unhandled message type", message.type);
298308
}

frontend/src/types/websocket.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ export interface TypingBroadcastMessage {
183183
};
184184
}
185185

186+
export interface ForceDisconnectMessage {
187+
type: "FORCE_DISCONNECT";
188+
payload: { message: string };
189+
}
190+
186191
export type ServerMessage =
187192
| RoomCreatedMessage
188193
| RoomUpdatedMessage
@@ -191,7 +196,8 @@ export type ServerMessage =
191196
| QueueTimeoutMessage
192197
| QueueLeftMessage
193198
| ErrorMessage
194-
| TypingBroadcastMessage;
199+
| TypingBroadcastMessage
200+
| ForceDisconnectMessage;
195201

196202
export type AnyWebSocketMessage = ClientMessage | ServerMessage;
197203

@@ -201,6 +207,7 @@ export const RATE_LIMIT = {
201207
} as const;
202208

203209
export const WS_CLOSE_CODES = {
210+
FORCE_DISCONNECT: 4000,
204211
AUTH_FAILED: 4001,
205212
RATE_LIMIT_EXCEEDED: 4001,
206213
INVALID_MESSAGE: 4002,

0 commit comments

Comments
 (0)