no affect on CORS enabling with NESTJS

Viewed 1746

I fail to enable the CORS for testing with the latest NestJS 8.0.6 and a fresh http + ws project. That said, I want to see the Access-Control-Allow-Origin in the servers response (so that the client would accept it). Here is my main.ts where I've tried 3 approches: 1) with options, 2) with a method, 3) with app.use. None of them works.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { microserviceConfig} from "./msKafkaConfig";

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { cors: true}); // DOESN'T WORK
  app.enableCors(); // DOESN'T WORK

  app.connectMicroservice(microserviceConfig);
  await app.startAllMicroservices();

  
  // DOESN'T WORK
  app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS,UPGRADE,CONNECT,TRACE');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Accept');
    next();
  });
  
  await app.listen(3000);



}
bootstrap();

Please, do NOT give me a lesson on how dangerous CORS (XSForgery) is if we accept all domains. there is enough material about that. And I'm well aware of it. This is about NestJS not replying the Access-Control-Allow-Origin element in the header.

The browser console reports:

Access to XMLHttpRequest at 'http://localhost:3000/socket.io/?EIO=4&transport=polling&t=Nm4kVQ1' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

In the chrome header inspection I see:

Request URL: http://localhost:3000/socket.io/?EIO=4&transport=polling&t=Nm4kUZ-
Referrer Policy: strict-origin-when-cross-origin
Connection: keep-alive
Content-Length: 97
Content-Type: text/plain; charset=UTF-8
Date: Mon, 20 Sep 2021 19:41:05 GMT
Keep-Alive: timeout=5
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en,de-DE;q=0.9,de;q=0.8,en-US;q=0.7,es;q=0.6
Cache-Control: no-cache
Connection: keep-alive
Host: localhost:3000
Origin: http://localhost:4200
Pragma: no-cache
Referer: http://localhost:4200/
sec-ch-ua: "Google Chrome";v="93", " Not;A Brand";v="99", "Chromium";v="93"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
EIO: 4
transport: polling
t: Nm4kUZ-

Does the Referrer Policy: strict-origin-when-cross-origin have an influence?

(btw, it works just fine with a simple express setup. So it cannot be my browser's fault.)

3 Answers

The enableCors and { cors: true } options are for the HTTP server (express or fastify). The URL given showing the CORS error came from a socket.io connection. To enable CORS for socket.io you need to use the options in the @WebsocketGateway() decorator, like

@WebsocketGateway({ cors: '*:*' })
export class FooGateway {}

Make sure to have both the host and the port set for the websocket cors as host:port

I had the same issue recently
and fixed it as the following in main.ts

in main.ts

    // import
    import { NestExpressApplication } from '@nestjs/platform-express';

    //in bootstrap() function
    const app = await NestFactory.create<NestExpressApplication>(AppModule);
    app.enableCors();
    app.setGlobalPrefix('/api/v1')
//in your Gateway service 

import { Socket, Server } from 'socket.io';
import {
    OnGatewayConnection,
    OnGatewayDisconnect,
    OnGatewayInit,
    SubscribeMessage,
    WebSocketGateway,
    WebSocketServer,
} from "@nestjs/websockets";
@WebSocketGateway(
    {
        path: "/api/v1/ws",
        serveClient: false,
        cors: {
            origin: `*`
        }
    })
export class AppGateway
    implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
    
    private logger: Logger = new Logger(AppGateway.name);

    ...
    afterInit(server: Server) {
        this.logger.log(`Init`);
    }

    handleDisconnect(client: Socket) {
        this.logger.log(`handleDisconnect: ${client.id}`);
        this.wss.socketsLeave(client.id);
    }

    handleConnection(client: Socket, ...args: any[]) {
        this.wss.socketsJoin(client.id)
        this.logger.log(`handleConnection: ${client.id}`);
    }
}
  
   //in your client side 

 this.socket = io("ws://localhost:3000", 
       {
        path: "/api/v1/ws",
        reconnectionDelayMax: 10000,
       }
);
// package.json
  "dependencies": { 
  "@nestjs/platform-socket.io": "^8.0.6",
  "@nestjs/platform-express": "^8.0.0",
  "@nestjs/websockets": "^6.1.0"
},
 "devDependencies": {
  "@types/socket.io": "^3.0.2",
  "@types/ws": "^7.4.7"
}

I deleted or changed transports argument in the array. It is from the frontend

    //FRONTEND FILE
    socket = io(BE_URL, {
      withCredentials: true,
      query: {
        token,
        isUserNew,
      },
      transports: ['websocket', 'polling'], // USE ['polling', 'websocket'] OR DELETED IT
      autoConnect: false,
    });



//BACKEND FILE
@WebSocketGateway({
  cors: { credentials: true, methods: ['GET', 'POST'], origin: ['http://host1', 'http://host2']},
  transports: ['polling', 'websocket'],
})

I read it - https://socket.io/docs/v3/client-initialization/#transports

One possible downside is that the validity of your CORS configuration will only be checked if the WebSocket connection fails to be established.

I really hope this answer saves you some time.

Related