6

I'm developing a Spring boot application using STOMP messaging over a websocket implementing an RPC pattern which exposes public (i.e.: without any need to be authenticated) and private methods.

Two public methods exists to register and authenticate the user, so I need to manually (programmatically) handle the user login, method accessed via a custom @MessageMapping operation.

The question is: how do I authenticate the user of the websocket session?

To make a similar thing using a normal MVC web application I would use a code similar to this:

@MessageMapping("/auth")
public Object auth() {
  List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_TEST");
  SecurityContextHolder.getContext().setAuthentication(new PreAuthenticatedAuthenticationToken(myname, "dummy", authorities));
}

but this seems not work in a websocket environment because when the user a method like this:

@MessageMapping("/myendpoint")
public Object myEndpoint(Param params, Principal principal) {...}

I get the error:

org.springframework.messaging.simp.annotation.support.MissingSessionUserException: No "user" header in message

So the question is: how do I manually authenticate the user in the auth() method so that in myEndpoint() the Principal parameter is correctly resolved?

Alessandro Polverini
  • 2,301
  • 19
  • 29
  • What does the message in your auth request look like? You may need to do something at a level higher like in a security filter in order to set the credentials there. I have been trying to solve something similar and that is what I did by passing in an auth token in on the connection URL. Let me know what you are trying and we can maybe flush it out a bit more. – Rob Baily Jul 19 '15 at 19:31
  • I've an internal mechanism to authorize users that involves a challenge sent by the server to the client. The client access the app only via the websocket and it's not web based so I can't rely on pre-authenticate the user via a web page hence I need to authenticate the websocket channel. – Alessandro Polverini Jul 20 '15 at 07:13
  • Still not quite clear. Are you sending something via STOMP after connection that the user must respond to via another STOMP message or some other method? Currently for a Spring WebSocket session the the user has to be set during the handshake phase (DefaultHandshakeHandler.determineUser()) and there are no setters for the Principal for a WebSocketSession. Spring STOMP uses the WebSocketSession to add the user to the STOMP header (in StompSubProtocolHandler.handleMessageFromClient()). I can post some code around this if you want although you may have to change your process some to use it. – Rob Baily Jul 20 '15 at 11:32
  • Yes, I've implemented an RPC pattern using STOMP: some methods are "public" (i.e.: no need to authenticate, just open the websocket and make calls), while others need the user to be authenticated, so some of the public methods are those used to register and authenticate. Feel free to ask details, in the mean time I'll try to better specify the question. – Alessandro Polverini Jul 20 '15 at 17:23
  • Hmm, I don't believe this pattern is supported. They say this about the @MessageMapping methods: "java.security.Principal method arguments reflecting the user logged in at the time of the WebSocket HTTP handshake." Based on this if they are anonymous at the handshake there is no way to do it. What about using a different endpoint for authenticated communication? You could use the public ones do get some auth token back and have them pass that back in on another endpoint. – Rob Baily Jul 20 '15 at 20:36
  • Too bad. I'll continue using a custom solution, but I'll consider your suggestion, thanks. – Alessandro Polverini Jul 21 '15 at 19:31

0 Answers0