Spring WebSocket and React SockJS Connection Issues

30 views Asked by At

I'm working on creating a user-to-user chat application using Spring and React, and I want to prevent anonymous users from connecting to the WebSocket. Additionally, I'm using OAuth2 Resource Server for client authentication with JWT tokens. My security configuration is implemented as follows:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(requests -> requests
                    .requestMatchers("/ws/**").permitAll()
                    .requestMatchers("/api/**").permitAll()
                    .requestMatchers("/secret/user").hasAuthority(USER)
                    .requestMatchers("/secret/admin").hasAuthority(ADMIN)
                    .anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())))
            .build();
}

Here is the WebSocket-related implementations:

public class WebAuthHandshakeInterceptor implements HandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Map<String, Object> attributes) throws Exception {
        // Auto rejecting for testing purpose
        boolean isAuthenticated = false;
        if (isAuthenticated) {
            return true;
        } else {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getBody().write("Authentication failed".getBytes(StandardCharsets.UTF_8));
            return false; // Reject the WebSocket handshake
        }
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Exception exception) {
        // TODO Auto-generated method stub
    }
}
@Configuration
@EnableWebSocketMessageBroker
@CrossOrigin(origins = "${allowedCrossOrigin}")
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Value("${allowedCrossOrigin}")
    private String allowedCrossOrigin;

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOrigins(allowedCrossOrigin)
                .addInterceptors(new WebAuthHandshakeInterceptor())
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic", "/queue");
        config.setApplicationDestinationPrefixes("/app");
    }
}

I have to permit all requests to the WebSocket endpoint; otherwise, it never reaches the custom HandshakeHandler to perform authentication. I didn't find any other way to send the client's token to the web server.

React connection to the endpoint:

const socket = new SockJS("http://localhost:7777/backend/ws");

There is no problem when the authentication succeeds, but when it gets rejected, problems start to show. The following pile of errors appears after the beforeHandshake returns false: Thrown errors by react.

Is there any way to terminate the SockJS gracefully after the authentication fails?

0

There are 0 answers