rstoyanchev / spring-websocket-portfolio

740 stars 443 forks source link

Add sample code for cross domain websocket implementation including authentication #57

Open rob-baily opened 9 years ago

rob-baily commented 9 years ago

@rstoyanchev and @rwinch, I have been doing research on how to handle cross domain websockets that need to handle authentication as well and I have yet to find any definitive answers in Spring documentation, on StackOverflow or other other internet sources. I've seen posts saying things like open the socket and then wait for an authenticated message and otherwise close the socket. The main issue seem to be a restriction from web based implementations on setting authentication information in the initial headers.

I believe in today's world this is a more common use case. I can see that in the example here it may be desirable to offer a quote service that others could include on their sites and give them information based on the user credentials and role.

I'd like to add some sample code to this (or by itself if that makes more sense) that others can use to follow and I wanted to get something that is considered secure. It also seems like we may need to consider something with or without SockJS since that appears to use iFrames in some cases. So if you have some advice about how to start this I would go ahead and take a crack at the code. I've done my own hacking to add an authentication token on the initial URL but that seems like it might not be the most secure option.

Sorry if this is wrong way to go about it but it seemed like a good avenue to get advice from the experts and help add to the community knowledge.

rob-baily commented 9 years ago

@rstoyanchev and @rwinch, I am guessing you are very busy but I was wondering if you had a chance to review this and see if it seemed worthwhile and if you had any suggestions. I have been trying to find information on this topic and have been unable to,

rstoyanchev commented 9 years ago

hi, was away on vacation. What do you need exactly or rather what isn't working? Regarding cross-domain, you can use WebSocketConfigurer to specify a list of origins.

rob-baily commented 9 years ago

No worries and hope you enjoyed it! The problem seems to be getting cross domain to work with authentication. From what I can tell there is no best practice for how to ensure that a cross domain socket request is authenticated, especially when STOMP is involved. It seems that you can only pass in information on the handshake for the websocket and you can't add an actual authorization header when you are using a web browser client. I did my own hack to pass credentials in on the handshake request and add a security filter before the built in user/password token authentication but that seems like it is not very secure. So if you have any insight on what would be the best way to handle the security there are many questions out there about how to best do this but no real great answers based on using the current Spring framework. Let me know if this makes sense.

rstoyanchev commented 9 years ago

From what I can tell there is no best practice for how to ensure that a cross domain socket request is authenticated, especially when STOMP is involved.

Hm I'm not sure what you mean. The authentication is standard Spring Security web URL-based security. It protects the WebSocket/SockJS endpoint regardless of what sub-protocol is used on top. In other words the use of STOMP vs anything else for higher level messaging should not matter.

It seems that you can only pass in information on the handshake for the websocket and you can't add an actual authorization header when you are using a web browser client.

Not sure what you mean by that either. Once authenticated, the WebSocket/SockJS session contains the Principal and you can apply authorization with Spring Security 4 based on that.

rob-baily commented 9 years ago

Sorry, let me see if I can explain a little bit more. In a cross domain situation on a web browser my understanding is that you can do an AJAX call to do some type of authentication to the server but you are not able to store any kind of token as a cookie that would be sent to the cross domain with a websocket request. You can set it up for AJAX calls using jQuery but not websocket handshakes that I can see. For example in this question use case 3 shows that you will not get a session id sent across under this scenario. My understanding is also that with the current web browser APIs for websockets there is no way to send an arbitrary HTTP header along with the handshake request. The Spring session works in a "same domain" situation as the browser is still automatically adding the cookies on the handshake request and so the normal filters added by Spring Security manage this. From what I can tell there is no way to pass anything on the handshake except by sending parameters in the URL. As an example I have done the Javascript code below to try and get around this limitation:

socket = new SockJS("http://" + host + "/subscribe?authentication=" + authToken);

or the same putting STOMP directly over a websocket:

var url = "ws://" + host + "/subscribe?authentication=" + authToken;
stompClient = Stomp.client(url);

and then I have to implement a custom security filter in order to handle this so then when the STOMP session starts. See this question where I put example code.

The crux of the problem I (and others) seem to be facing is that cross domain authentication does not have a standard way to be propagated to a STOMP session due to limitations on being able to adjust headers on the handshake. With STOMP integration this must be set on the handshake in order for the Principal to be associated with the STOMP session as there is no other way to set it later based on any custom auth protocol from what I can tell.

Hope this makes sense. I can post code on GitHub that illustrates it if you think that might help.

rob-baily commented 9 years ago

I was out of town for a while and now I am back and I wanted to check to see if my last description made more sense or if I need to post some code to show it. Please let me know when you can.

rstoyanchev commented 9 years ago

Sorry but I don't fully understand still. Cookie based authentication should work. Have you tried that and failed or is your goal to authenticate in a way other than using a cookie?

btrajkovski commented 9 years ago

I have kind of a similar issue. We are using oAuth2 with Spring Security 3.2.5 and Spring 4.2.0. The problem I am facing is that Spring WebSocket is unable to get Principal, and my connection is not authenticated(in DefaultHandshakeHandler - determinateUser, Principal is null). Is the problem in using old version of Spring Security and how can this be solved?

EDIT: Solved, cause by local problem, not spring

arpiticx commented 7 years ago

hi, I am been having an issue described in SO. https://stackoverflow.com/q/43999665/7354124 Any help is appreciated