0

My goal is to authenticate the WebSocket CONNECT frame. I wish to be able to initialize Authentication user = ... by using X-Auth-Token.

TL:DR

I use the X-Auth-Token header. How the current authentication works:

  1. User hit POST /login endpoint with Form Data username and password.
  2. The response header will contain the key X-Auth-Token.
  3. If you hit any REST endpoint with X-Auth-Token the server will recognize the user.
  4. The issue is how to get Authentication from X-Auth-Token in the WebSocket CONNECT frame.

The current solution is to use JWT, however, one of the requirements for this project is a user should be able to invalidate the session. For JWT to be able to do that, the JWT should be a stateful, reference to this SO's the question

@Configuration
@EnableWebSocketMessageBroker
// see: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket-stomp-authentication-token-based
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketAuthenticationConfig implements WebSocketMessageBrokerConfigurer {
  @Override
  public void configureClientInboundChannel(ChannelRegistration registration) {
    registration.interceptors(new ChannelInterceptor() {
      @Override
      public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor =
                MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if (StompCommand.CONNECT.equals(accessor.getCommand())) {
          String sessionId = accessor.getFirstNativeHeader("X-AUTH-TOKEN");
//          Authentication user; // How to get Authentication from X-Auth-Token?
//          accessor.setUser(user);
        }
        return message;
      }
    });
  }
}

What I did:

  1. I change Cookie-based authentication by letting the session be provided in a header.
// see: https://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-rest
@Configuration
// Override HttpSession's Filter, in this instance Spring Session is backed by Redis.
@EnableRedisHttpSession
public class HttpSessionConfig {

  // Default connection configuration, to localhost:6739.
  @Bean
  public LettuceConnectionFactory connectionFactory() {
    return new LettuceConnectionFactory();
  }

  // Tell Spring to use HTTP headers, X-Auth-Token.
  @Bean
  public HttpSessionIdResolver httpSessionIdResolver() {
    return HeaderHttpSessionIdResolver.xAuthToken();
  }
}
  1. logic to CONNECT and SUBSCRIBE
const X-Auth-Token = "" // get from the POST `/login` endpoint

const onConnectCallback = () => {
  const destinations = ["/topic/channel/1", "/user/queue/messages"];
  for (let i = 0; i < destinations.length; i++) {
    stompClient.subscribe(destinations[i], (payload) => {
      // receiveMessageCallback
    });
  }
};

const stompConfig = {
  brokerURL: "ws://localhost:8080/chat",
  connectHeaders: {
    "X-Auth-Token": X_Auth_Token,
  },
  onConnect: onConnectCallback,
};
const stompClient = new StompJs.Client(stompConfig);
stompClient.activate();

Reference

I am worried that X-Auth-Token is not supported in WebSocket because based on SO's answer there is no API to retrieve session by id

Younes
  • 462
  • 7
  • 15
Jason Rich Darmawan
  • 1,607
  • 3
  • 14
  • 31
  • I'm not sure how much flex you have with your design, but this article details nicely why to avoid using JWTs as session tokens: https://developer.okta.com/blog/2017/08/17/why-jwts-suck-as-session-tokens It may inspire a different architecture that allows you to keep session management independent from authorization. (Apologies that this doesn't directly answer your question.) – jzheaux Jan 28 '21 at 20:51
  • @jzheaux I did not use JWT, I use the Spring Session. I use `X-Auth-Token` header instead of the `JSESSION` inside the `Cookie` header. The `X-Auth-Token` works out of the box for the REST endpoint, while it does not for the WebSocket endpoint. – Jason Rich Darmawan Jan 29 '21 at 02:13
  • I don't know what you mean, then by: "one of the requirements for this project is a user should be able to invalidate the session. For JWT to be able to do that," - what do you mean by "that" if not "maintain the session"? – jzheaux Jan 29 '21 at 15:49
  • @jzheaux By "that" I mean "maintain the session". But as per my knowledge, `JWT` supposedly to be stateless, therefore a user can't invalidate his token unless I store the invalidated token in a Redis, making it to be stateful. Not only, JWT require more bandwidth per HTTP handshake but it also need a Redis to be able to invalidate a token. While session do it out of the box, because everything is either stored in the spring app memory or in the redis. – Jason Rich Darmawan Jan 29 '21 at 16:17

0 Answers0