spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.23k stars 37.98k forks source link

Better documentation on WebSockets and support for token based authentication [SPR-14380] #18953

Open spring-projects-issues opened 8 years ago

spring-projects-issues commented 8 years ago

marc schipperheyn opened SPR-14380 and commented

Spring Websocket, basically relies on HTTP Session Cookie based authentication. Custom header tokens, provided when creating a WebSocket are ignored. STOMP login,passcode are also ignored for whatever reason.

After much trial and error and tons and tons of Stackoverflow research, I finally got that Spring WebSocket expects you to create an authenticated session through "normal" website interactions and then use the resulting cookies as an authenticated way to interact with the WebSocket.

So, Spring WebSocket makes the implicit assumption that the WebSocket is created within a webpage where session creation and authentication have basically already taken place through "normal" website interactions. This assumption is what makes Spring WebSocket documentation and examples so confusing.

In a mobile phone scenario. we can get this "normal" website interaction also through a REST endpoint, and then use the resulting cookies to connect with the WebSocket (this suggestion is not documented).

Only, the mobile environment is not such a cut and dried place as the modern web browser is. Not all Mobile Environments supply cookies through their WebSocket connections. Mine doesn't by default, also not documented by the mobile platform vendor, and I'm still trying to hack my way around that.

I would say that most websocket implementations out there assume header based authentication. It would make life a lot easier. The whole concept of cookies is a browser concept. Headers are much more transparent and clearcut.

So, let me take you through that scenario: getting an existing Spring application to work on websockets.

So, this is just a little list of the issues I ran into. There are issues on all sides: server, webserver, session, security, frontend.

The huge amount of time spent on this stuff could have been significantly reduced if there had been clear documentation on the concepts and assumptions of Spring WebSockets. It would be nice if I knew that step 1 should result in a 101 status. If it doesn't, something is wrong.

Some suggestions:

So, this is a bit of a rambling list of suggestions. I hope it's useful nonetheless.


Issue Links:

2 votes, 7 watchers

spring-projects-issues commented 8 years ago

Rossen Stoyanchev commented

First of all appreciate the feedback, the sentiment of frustration also is noted. That said there is plenty of documentation in Chapter 26. I won't say it's perfect nor that it can't be better but to say it's terrible is a bit unfair. In any case I'm sorry for any time lost figuring things out. Let's discuss ways to make it better.

WebSockets 101 As stated in the overview, this is beyond the scope of the Spring reference. There is plenty of that out there. I don't mind however including a paragraph before such a statement if you would you like to contribute such a paragraph somewhere in section 26.1, Or at least take a look and suggest where it fits?

SockJS upgrade The SockJS overview explains the sequence of requests with the initial "/info" request followed by a single transport request for WebSocket vs many more polling/streaming requests for HTTP fallback transports. I can make another pass at that but it would be even more helpful if you could suggest concretely what would make that text clearer for you?

How Spring WebSocket works The Spring WebSocket support describes the process of registering handlers first and then some information on how it works here with the combo of a HandlerMapping + RequestUpgradeStrategy. Would you provide comments on how to improve that? Re-structure, add more information, etc.?

Authentication Starting with plain WebSockets (incl. STOMP over WebSocket), the premise is that the authentication mechanism is an orthogonal concern and that you should be able to use any existing HTTP authentication mechanism. The WebSocket upgrade request (the handshake) is just another HTTP request, so secure it just like you would any other part of your web site. This is why the existing Authentication section is relatively short.

Now with SockJS, it is true that the JavaScript SockJS client does not allow sending headers because the IFrame and JSONP transports which support older IE versions are unsafe for CORS. This is also why when CORS is enabled in our server-side implementation, we disable such transports. This is also explained in the docs for SockJS.

That said Spring's Java SockJS client does allow associating HTTP headers with every SockJS HTTP request so you should be able to send cookies or other authentication headers. Again this should be no different than using the RestTemplate for other secured parts of your web site. Note that separately there is a request for Spring Security to support authentication at the STOMP level and such authentication makes sense for STOMP over TCP. For WebSocket however we operate in the context of HTTP where we have existing HTTP-based authentication options.

To be clear I see the need to expand the documentation in this regard as a result of this discussion. For example to better and clearly state why we don't do authentication at the STOMP level. Also I can see creating a new sub-section to cover security for WebSockets and SockJS first and then update the existing sub-section under STOMP messaging in addition to that. Concrete suggestions welcome.

Web server integration

Presumably this is not what you're looking for. Can you clarify what you would like to see and where would you like to see it?

Spring Security and Spring Session integration

These are maintained in the docs for Spring Security and Spring Session. You have seen them I presume? Perhaps just looking for something in the Spring Framework reference pointing in that direction? That could be part of an improved section on security and authentication.

What happens when a client disconnects

You have seen this I presume? Suggestions?

Debugging

Plenty of time was spent on fine-tuning logging at INFO, DEBUG and TRACE levels which is quite hard to get right in a messaging system due to the volume. Beyond that we have sections on Configuration and performance and also some facilities for Runtime monitoring. I'm open to suggestions for a dedicated section on debugging though. That would be good to have.

spring-projects-issues commented 8 years ago

marc schipperheyn commented

Ok, yes, I agree. to say it's terrible was unfair. Sorry about that. There is plenty of documentation. it's just that some key conceptual elements are missing which cost me an enormous amount of time.

Websocket Concepts The key is "concept". Once you understand how things are expected to work and integrate it becomes easier to understand why things are not working as expected.

So I do think a paragraph that discusses the key concepts is crucial. E.g. even if it goes beyond the scope of Spring documentation, the 101 upgrade aspect is key and the server works differently if you add .withSockJS(), which added tons of confusion. Alternatively, in stead of adding this the default message "Hi!" style message with a 200 status to a connect attempt against the (wrong) /ws endpoint instead, you could consider adding a message "Hi! if you ended up here trying to execute a websocket connect, you should add /websocket to your url". Would save hours of frustration and would eliminate the need for documentation.

I will see if I can find the time to shoot some docs suggestions while everything is still fresh.

Security My biggest conceptual problem was the idea that Spring Websocket combined with Spring Security assumes the use of cookies as a means of authentication. I can't stress enough how unorthodox this feels in a mobile world where everything tends to work with headers. This authentication paragraph definitely needs to explain this clearly. That was just the biggest part of the two weeks lost on my side. And believe, I read all the documentation (some of it outdated and contradictory), all of the examples and all of the stackoverflows on the subject. I was that desperate. I have done a lot of configuration with Spring over the years, but it's something you tend to do once or twice in a project, and then forget about, so my own lack of skill in this area certainly hampered me here.

Since all headers are ignored on the connect request you kind of end up between a rock and a hard place. In my case (react-native), when I finally got my head around it thanks to your comment on stackoverflow, I had to use an undocumented attribute on the WebSocket connection that allowed me to specify the session and rememberme cookies and I got in.

RabbitMQ I think it's also a good idea to clearly explain where Spring WebSocket ends and RabbitMQ begins. It's not clear what kind of features Spring WebSocket doesn't support and RabbitMQ does. Could Spring Websocket be considered for production purposes or should it be considered more of a testbed?

Sessions One of the aspects that is also not entirely clear to me yet is how to manage sessions correctly. Should these be considered stateless for instance?

Hibernate/JPA And lastly, I had trouble integrating hibernate and applied an ugly workaround #18960.

Webserver Nginx for instance, and I assume others as well, need specific configuration to make it pass through 101 upgrade requests. Which is why it would be good IMHO to state clearly that the result of an initial Connect should be a 101 status and anything else is wrong.

spring-projects-issues commented 7 years ago

Rossen Stoyanchev commented

marc schipperheyn, under #19254 I've added support for token-based authentication at the STOMP level. I've also completely redone the section on authentication. Would you mind taking a look at the updated Authentication section and the new Token-Based Authentication section right below it?

Better yet if there is any chance you could try using the token-based authentication at the STOMP level that would be even better. There is currently a 4.3.5.BUILD-SNAPSHOT and next week 4.3.5 will be released. Thanks!

Aside from that I would still like to take you suggestions above and create a topical guide such as the one for Spring Security. It's outside the main reference documentation and it's only meant to be read once to get the idea.

spring-projects-issues commented 7 years ago

Andrii Plotnikov commented

The links are broken, since inside repository they were moved. here are proper link Authentication and Token-Based Authentication Also, those are not parsable by google bot and do not show up in google search, so it would be good to update them in https://spring.io/docs or https://spring.io/guides

spring-projects-issues commented 7 years ago

Rossen Stoyanchev commented

Those are direct source code links that I probably shouldn't have used. Those sections are part of the reference documentation.

spring-projects-issues commented 7 years ago

Steve Roy commented

There is some great information in this ticket both from the reporter, Marc, and Rossen. Thank you both. This ticket is almost a form of documentation itself. :)

I see this ticket is backlogged for 5.x. I would suggest prioritizing the documentation of the following items to at least help prevent developers from pursuing unproductive paths.

These things could be fairly quick mentions and could help with some of the current confusion.

— fyi - I also stumbled upon another issue around the Token-based Authentication and logged this ticket . #20418

spring-projects-issues commented 7 years ago

Rossen Stoyanchev commented

The ticket is marked 5.x but changes were made already including a comprehensive update of the authentication section and the new token-based authentication section which explains many of the things on your list. Can you please be more specific what's missing and where you would like to see it?

spring-projects-issues commented 7 years ago

Steve Roy commented

I read the changes over - thank you.

It looks like these questions were answered:

And these remain outstanding:

spring-projects-issues commented 7 years ago

Rossen Stoyanchev commented

"Differences in behavior with socksJS enabled and disabled" appears under both answered and outstanding. A typo I presume? Also since you've had a recent, specific experience, would you be able to submit a PR suggesting those changes? I would be happy to process and improve as needed.

spring-projects-issues commented 5 years ago

Julian Faude commented

Hi all,

as has already been said this ticket has been incredibly helpful to get a better understanding of Spring Websocket Support and Authentication/Authorization. Thanks!

Unfortunately, as I understand, for web (STOMP over Websocket) there is currently no out of the box solution except for cookies, am I getting this right?

Also, there is another issue I cannot get my head around: An incoming Websocket Request is supposed to be authenticated during the initial handshake. However, a websocket stays open for a potentially long time, way longer than a regular HTTP request. When using token based authentication, the token which has been presented during the handshake expires at some point. Ideally, at that point the client should have to re-authenticate (by presenting a renewed token). Is this in any way addressed in the current implementation?

Thanks again!