Search code examples
expresssocket.ionestjs

How to set socket.id as an HTTP cookie?


Task:
I'm using NestJS and trying to find information on how to set the socket.id as a cookie for every client who has connected to the server.

Simplified code slices:
I found this code example in the documentation:

const io = new Server(httpServer, {
  cookie: true
});

// is similar to

const io = new Server(httpServer, {
  cookie: {
    name: "io",
    path: "/",
    httpOnly: true,
    sameSite: "lax"
  }
});

So, since I don't use Express.js(and native Node.js), but use Nest.js, I've tried to adjust the above-mentioned code as follows:

// event.gateway.ts file

@WebSocketGateway({
    namespace: "api/chat",
    cors: {
        origin: "*",
    },
})
export class EventGateway
    implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit
{
    @WebSocketServer()
    server: Namespace<IClientToServerEvents, IServerToClientEvents>;

    afterInit(server: Namespace) {
        console.log("server: ", server);
        server.server.engine.opts.cookie = {
            name: "io",
            path: "/",
            httpOnly: true,
            sameSite: "lax",
        };
    }

    // other code...
}

There were no typescript errors when checking the code, and there were no javascript errors after creating the application. I tried to find the Set-Cookie header when initializing the connection. So, this is it, but as you can see they weren't set: enter image description here enter image description here

Possible workarounds:

  1. I comprehend that I can set a cookie on the front end after initializing the socket connection, but I want to use the HttpOnly flag.
  2. Also, there is an idea to do one more HTTP request after a socket connection and send the socket.id in the request body. After this call - I'll be able to set this value as the HttpOnly cookie via Backend. But I want to believe, that there is a way with no tricky actions.

P.S:
It looks like socket cookies are different from the HTTP cookies. So, please tell me, which way I should use to set socket.id value as an HTTP cookie?


Solution

  • It seems like there is no well-trodden path to follow. And I found out that Websocket cookies are different from HTTP cookies.

    So, the best approach to handle with this task is:
    After establishing the websocket connection, in websocket "on connect" event, the frontend should simply sends an HTTP request, e.g., http://localhost3000/api/set-socket-id, and lay the socketId in the request body. Then, the backend will set the socketId as an HttpOnly cookie.

    Here is code example:

    // frontend
    this.socket.on("connect", async () => {
        await fetch("http://localhost3000/api/set-socket-id", {
            method: "POST",
            credentials: "same-origin",
            headers: {
                Accept: "application/json",
                "Content-Type": "application/json",
            },
            body: JSON.stringify({ socketId: this.socket.id }),
        });
    });
    
    // backend
    @Post("set-socket-id")
    @UseGuards(AuthGuard)
    setSocketId(
        @Req() request: Request,
        @Res() response: Response,
        @Body("socketId") socketId: string
    ) {
        response.cookie("socket_id", socketId, {
            path: "/",
            httpOnly: true,
            sameSite: "lax",
        });
        response.sendStatus(200);
    }