Search code examples
javanode.jsvue.jswebsocketmicronaut

How do I enable cross-origin websocket connections in Micronaut?


I'm trying to implement a new Micronaut microservice that will be a websocket server, let's say it runs on http://localhost:8081 and the websocket is on ws://localhost:8081/ws.

The client will be the Vue.js UI running locally on a different domain, say http://dev.domain.com:8080.

When I click the UI button that will trigger the websocket connection and transmission of data, I get the following in Chrome:

Common.umd.js:8 WebSocket connection to 'ws://localhost:8081/ws' failed:

and the following in Firefox:

GET ws://localhost:8081/ws [HTTP/1.1 403 Forbidden 412ms]

Firefox can’t establish a connection to the server at ws://localhost:8081/ws. Common.umd.js:formatted:3191:34

In Firefox it gives a 403 error, but in Chrome under the network tab it just says that the request to ws://localhost:8081/ws has Finished, without a status code. I'm not really sure what to make of this, but the 403 must be coming from the Micronaut server, correct?

The Micronaut websocket server code generally looks like (I left out the details because there is nothing mentioned in the Micronaut server access logs about a 403, meaning the code isn't even reached):

@ServerWebSocket("/ws")
public class Server {

  /** Class logger. */
  private static final Logger log = LoggerFactory.getLogger(Server.class);

  /** Micronaut application context. */
  @Inject ApplicationContext ctx;
  
  /** Service configuration. */
  @Inject Configuration configuration;

  /** WebSocket broadcaster. */
  @Inject WebSocketBroadcaster broadcaster;

  /** The RabbitMQ message producer. */
  @Inject ApiProducer producer;
  

  /**
   * Handler for connection open event.
   *
   * @param session - the created WS session.
   * @return Publisher for the startup confirmation message.
   */
  @SuppressWarnings("static-method")
  @OnOpen
  public Publisher<String> onOpen(WebSocketSession session) {
    ...
  }

  /**
   * Handler for message received event.
   *
   * @param message - the command payload
   * @param session - current WS session
   * @return Publisher for the response message.
   */
  @SuppressWarnings("unchecked")
  @OnMessage
  public Publisher<String> onMessage(String message, WebSocketSession session) {
    ...
  }

  public Mono<String> onNotification(String notification, UUID correlationId) {
    ...
  }

  /**
   * Handler for connection close event.
   *
   * @param session - the WS session to be closed.
   */
  @SuppressWarnings("static-method")
  @OnClose
  public void onClose(WebSocketSession session) {
    ...
  }
}

On the Vue.js side, we are using the vuex createStore method in conjunction with the VueNativeSock module from the vue-native-websocket-vue3 package.

Also on the Micronaut side, I have tried enabling processing of CORS requests using the following in the application.yml file:

micronaut:
  application:
    name: application-name
  server:
    cors:
      enabled: true
    port: 8081

However, this still doesn't work.

Some additional context: when I use http://localhost:8080 as the UI domain and leave everything else the same, it connects and sends the data without issue. The problem only arises when the UI has a different domain, which would also be the case in production.

Any help is appreciated.


Solution

  • So I ended up finding the answer here: https://github.com/micronaut-projects/micronaut-core/issues/8560 in sdelamo's first Jan 11 comment.

    I needed to use:

    micronaut:
      application:
        name: application-name
      server:
        cors:
          enabled: true
          configurations:
            ui:
              allowed-origins: "http://dev.domain.com:8080"
        port: 8081
    

    This contradicts the documentation I read, which said that the configurations property was used to restrict cors access to specific domains not allow it.