I can't use STOMP websocket in hybrid react app but able to use it in web version

I have a traditional web react app and built it to apk through capacitor, the authentication in http routes is done by a jwt in http only cookie, below are some configs I needed to do at capacitor config to use cookies :

  plugins: {
    "CapacitorCookies": {
      "enabled": true
    },
    "CapacitorHttp": {
      "enabled": true
    }
  }

The problem is that I can use my cookies to get auth in http but not in websockets (it only occurs in apk not on web version of app), below I’m connecting to websockets in react:

  public initializeWebSocket() {
    const socket = new WebSocket(this.wsUrl);

  
    this.stompClient = Stomp.over(socket);
    this.stompClient.connect(
      {},
      (frame) => {
        console.log('Connected: ' + frame);
        this.stompClient?.subscribe('/user/queue/notifications', (message) => {//web socket notificacoes
          console.log("mensagem pura do websocket")
          console.log(message)
          if (message.body) {
            console.log("MENSAGEM DO WEB SOCKET");
            console.log(message.body);
            this.notficationThrower(JSON.parse(message.body) as UserNotificationDTO);//isso executara o call back que o componente react passou como parametro de onNotificationMessage
          }
        });
      },
      (error) => {
        console.error('STOMP error: ', error);
        alert('STOMP error: ' + error)
      }
    );
  }

And below my backend websocket config:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private TokenService tokenService;



    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic","/queue"); // /topic é global, qeue é privado
        config.setUserDestinationPrefix("/user");
        config.setApplicationDestinationPrefixes("/app"); // Prefixo para mensagens enviadas do cliente
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*")
                .addInterceptors(httpSessionHandshakeInterceptor());
    }

    @Bean
    public HandshakeInterceptor httpSessionHandshakeInterceptor() {
        return new HandshakeInterceptor() {
            @Override
            public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
                if (request instanceof ServletServerHttpRequest) {
                    ServletServerHttpRequest servletServerRequest = (ServletServerHttpRequest) request;
                    HttpServletRequest servletRequest = servletServerRequest.getServletRequest();
                    String token = tokenService.recoverToken(servletRequest);
                    if(token==null){
                        return false;
                    }
                    if(tokenService.validateToken(token)==null){
                        return false;
                    }
                    attributes.put("username", tokenService.getUsernameFromJWT(token));
                }
                return true;
            }
            @Override
            public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
            }
        };
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {//autenticar websocket
        registration.interceptors(new ChannelInterceptor() {
            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {
                StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

                if (accessor != null) {
                    String username = (String) accessor.getSessionAttributes().get("username");
                    UserDetails userDetails = userRepository.findByUsername(username);
                    UsernamePasswordAuthenticationToken authentication =
                            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    accessor.setUser(authentication);
                }
                return message;
            }
        });
    }
}

Again… I’m able to connect to websocket in the built react app (vite) but I can’t connect to websocket in built apk app (capacitor) since the cookie falling at:

    public String recoverToken(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("token".equals(cookie.getName())) {
                    return cookie.getValue(); // Retorna o valor do cookie
                }
            }
        }
        return null; // Retorna null se o cookie não for encontrado
    }

is null, but for other http requests the cookie of capacitor is not null

The problem is not in CORS config, for some reason my cookies are getting null when I connect to my websocket

First off, it’s not recommended to use Cookies in a Capacitor app :upside_down_face: See Capacitor iOS / Cookie Authentication / capacitor/http.

I am not familiar with STOMP (looks to be a simple text-oriented message protocol over WebSockets), but the way our app is setup with WebSockets, there is a standard HTTPS request that gets sent off first for authorization (the request contains the channel and socket ID). The response returns an auth key to authenticate with the WebSocket server. Is that how your setup is working?

A wild guess is that the Capacitor HTTP plugin isn’t intercepting the WS request so the cookies aren’t getting added to the request.

The Capacitor HTTP plugin works well for cookies when they’re used for authentication on a website. For example, I have a Drupal backend and use cookie authentication in Capacitor.

However, as the link by @twestrick shows, you can easily have problems if you’re doing anything other than the most standard uses of cookies.

Since you already have a JWT, I would consider storing it somewhere other than a cookie (such as localStorage). Then you won’t need the http plugin at all.