fix: WebSocket auth always fails — client object has no URL property

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 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-13 11:16:03 -04:00
parent d74bb28960
commit c4d7ad1833
3 changed files with 7 additions and 6 deletions

View File

@ -6,9 +6,10 @@ import { WsException } from '@nestjs/websockets';
export class WsAuthGuard { export class WsAuthGuard {
constructor(private jwt: JwtService) {} constructor(private jwt: JwtService) {}
validateClient(client: any): { sub: number; email: string } | null { validateClient(client: any, req?: any): { sub: number; email: string } | null {
try { 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'); const token = url.searchParams.get('token');
if (!token) throw new WsException('No token'); if (!token) throw new WsException('No token');
return this.jwt.verify(token) as { sub: number; email: string }; return this.jwt.verify(token) as { sub: number; email: string };

View File

@ -16,8 +16,8 @@ export class SftpGateway implements OnGatewayConnection, OnGatewayDisconnect {
private wsAuth: WsAuthGuard, private wsAuth: WsAuthGuard,
) {} ) {}
handleConnection(client: any) { handleConnection(client: any, req: any) {
const user = this.wsAuth.validateClient(client); const user = this.wsAuth.validateClient(client, req);
if (!user) { if (!user) {
client.close(4001, 'Unauthorized'); client.close(4001, 'Unauthorized');
return; return;

View File

@ -15,8 +15,8 @@ export class TerminalGateway implements OnGatewayConnection, OnGatewayDisconnect
private wsAuth: WsAuthGuard, private wsAuth: WsAuthGuard,
) {} ) {}
handleConnection(client: any) { handleConnection(client: any, req: any) {
const user = this.wsAuth.validateClient(client); const user = this.wsAuth.validateClient(client, req);
if (!user) { if (!user) {
client.close(4001, 'Unauthorized'); client.close(4001, 'Unauthorized');
return; return;