From c4d7ad1833e68698e3e13cdffc597509b65c9dc9 Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Fri, 13 Mar 2026 11:16:03 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20WebSocket=20auth=20always=20fails=20?= =?UTF-8?q?=E2=80=94=20client=20object=20has=20no=20URL=20property?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the NestJS ws adapter, the JWT token URL is on the HTTP upgrade request (second arg to handleConnection), not on the WebSocket client object. client.url was undefined, new URL(undefined) threw, catch returned null, and every connection got 4001 Unauthorized. Fix: Pass the IncomingMessage req to validateClient and prefer req.url. Co-Authored-By: Claude Opus 4.6 --- backend/src/auth/ws-auth.guard.ts | 5 +++-- backend/src/terminal/sftp.gateway.ts | 4 ++-- backend/src/terminal/terminal.gateway.ts | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/src/auth/ws-auth.guard.ts b/backend/src/auth/ws-auth.guard.ts index 207db60..1f931d3 100644 --- a/backend/src/auth/ws-auth.guard.ts +++ b/backend/src/auth/ws-auth.guard.ts @@ -6,9 +6,10 @@ import { WsException } from '@nestjs/websockets'; export class WsAuthGuard { constructor(private jwt: JwtService) {} - validateClient(client: any): { sub: number; email: string } | null { + validateClient(client: any, req?: any): { sub: number; email: string } | null { try { - const url = new URL(client.url || client._url, 'http://localhost'); + const rawUrl = req?.url || client.url || client._url; + const url = new URL(rawUrl, 'http://localhost'); const token = url.searchParams.get('token'); if (!token) throw new WsException('No token'); return this.jwt.verify(token) as { sub: number; email: string }; diff --git a/backend/src/terminal/sftp.gateway.ts b/backend/src/terminal/sftp.gateway.ts index 94b892c..7384845 100644 --- a/backend/src/terminal/sftp.gateway.ts +++ b/backend/src/terminal/sftp.gateway.ts @@ -16,8 +16,8 @@ export class SftpGateway implements OnGatewayConnection, OnGatewayDisconnect { private wsAuth: WsAuthGuard, ) {} - handleConnection(client: any) { - const user = this.wsAuth.validateClient(client); + handleConnection(client: any, req: any) { + const user = this.wsAuth.validateClient(client, req); if (!user) { client.close(4001, 'Unauthorized'); return; diff --git a/backend/src/terminal/terminal.gateway.ts b/backend/src/terminal/terminal.gateway.ts index 418337f..958d40f 100644 --- a/backend/src/terminal/terminal.gateway.ts +++ b/backend/src/terminal/terminal.gateway.ts @@ -15,8 +15,8 @@ export class TerminalGateway implements OnGatewayConnection, OnGatewayDisconnect private wsAuth: WsAuthGuard, ) {} - handleConnection(client: any) { - const user = this.wsAuth.validateClient(client); + handleConnection(client: any, req: any) { + const user = this.wsAuth.validateClient(client, req); if (!user) { client.close(4001, 'Unauthorized'); return;