Authenticating Sockets

Verify the JWT on Connect

HTTP guards do not run for the WebSocket handshake. Verify the token in handleConnection (or a Socket.IO middleware) and disconnect strangers. Also check that the user owns the order before joining its room.

@WebSocketGateway({ namespace: '/orders' })
export class OrdersGateway implements OnGatewayConnection {
  @WebSocketServer() server: Server;

  constructor(private readonly jwt: JwtService, private readonly orders: OrdersService) {}

  async handleConnection(client: Socket) {
    try {
      const token = client.handshake.auth?.token as string;
      client.data.user = await this.jwt.verifyAsync(token);
    } catch {
      client.emit('error', { message: 'Unauthorized' });
      client.disconnect(true);
    }
  }

  @SubscribeMessage('order:track')
  async track(@MessageBody() { orderId }: { orderId: string }, @ConnectedSocket() client: Socket) {
    const user = client.data.user;
    await this.orders.findOneForUser(orderId, { id: user.sub, roles: user.roles });   // throws if not owner
    await client.join(`order:${orderId}`);
    return { event: 'order:tracking', data: { orderId } };
  }
}

Tip: With several API instances behind a load balancer, add the Socket.IO Redis adapter (@socket.io/redis-adapter) so server.to(room).emit() reaches clients connected to other instances.