-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: define Signal K security protocols #506
Conversation
The base url for login/logout/validate should be a subdir of
|
From an application perspective it would be good to have a way to determine if auth is needed. For WebApps hosted on the Signal K server this is not a big issue, as getting access to the app in the first place would be restricted by authentications anyway, but if I have a native app or app hosted at another source looking to connect to a delta stream it would allow the app to be more proactive in getting credentials from the user or even caching them. |
@rob42 Not sure I like the auto renew of a token. I would think that if a token is expired, then the client must re-authenticate. For example, with #505, if I give approve a buddies device for 24 hours, then it should stop working after 24 hours. He would have to request access again to get a new token. |
I do like |
auto-renew is the only really practical way to handle the token expiry. You dont want your instruments popping up a login at the critical moment :-) The case of 24 hours access only is better handled by having a login expiry time. So it auto-renews until time/age x, then requires login again. like max password age limits. eg - if a new login would be valid, autorenew, otherwise login. |
Definitely does not work for me. Why give out a token that expires if you're not going to let it expire. I would never do this in the real world (Financial Services in my case), would not pass a security audit. For an instrument on your boat, give out a token that never expires. |
How long is the max token age in your system? Mine is 1 hour. If you connect within 1 hour it auto-renews, if not you login again. In financial services (I do that too) I set age at 30 min and a max-renew at 12 hours so a login is required twice a day anyway, or anytime you are idle more than an 30 min. |
This is exactly the kind of thing I don't think we should get into here. This should be up to the server. I've specifically separated out user login and device access. I think of device access more like giving API access to a client. In this case you would give out long living tokens. There's no "login" here, no username and password. The only way to get a token is for the device to ask for one and get approved. Also not sure I understand how the client is supposed to use For HTTP clients, you can alway renew their token if you want. Just send a new cookie. The same with WS (or other protocol clients). They should always be prepared to get a message with a new token without having to make a special call. |
I agree the handling of token expiry and renewal is a server implementation issue. I use the validate() call when I have an app that needs to maintain access but is not actively sending anything, eg an anchormonitor widget. It periodically checks/renews its token by making a validate call. I found that after a browser app subscribes it makes no tx messages, only rx via the ws. |
Had not though of case of a read-only WS client. But why not just close the socket when a token expires. A ws client should have reconnect logic anyway (server restarts, etc.) Socket gets closed, then reconnect and re-authorize. |
In my case the token is created by the user (who logs in) so reconnect causes the login to pop again. I found that the browser has to do a kind of keep-alive to make it work nicely. Its tied up with the situation where you cant tell if a browser has gone away or is just quiet in some communication error cases. |
What features here are specifically related to JWT? Like, if you leave out the word JWT and say token would everything work? |
I have a hard time grasping why you would automatically renew the original authorization when it is timing out. Why not just issue a longer lived token? Is the use case "idle timeout", eg. invalidate the authorization if the client goes inactive for a certain period? |
we should always use token instead of JWT. The token is expected to be JWT, but could be anything - its server implementation dependent. The reason I use auto-renew isidle timeout - because I dont know how long the client requires the token when I issue it, and I dont want valid tokens hanging around if they will not be used. This way the tokens only renew for active users. |
@rob42 your scenario and the need to refresh the token assumes that the validity period is embedded in the token and the server is stateless. If you just use a random token and the server keeps track of issued tokens and their last used time you can achieve what you want at will. Even with validity period embedded in the token you can achieve this with serverside last use bookkeeping and blacklisting. Kinda silly to choose to do so, but it would work, with no extra functional requirements on the client. BTW Note that the spec says nothing about validity periods and if we do away with JWT there is even less implied logic related to the token. So I think a better way to think about this is do we need to build in a token exchange/update mechanism, whereby the server may issue a new token and the client must update its token. For http cookies this is sort of easy - if the client is active you can update the cookie on the fly. For ws less so, unless you force the client to reconnect, which is a nuisance and may cause an otherwise ok client to loose some updates. I don't like calling /validate being the client's responsibility - how would a client know how often it should call that? Every 10 seconds? Every hour? The question is: do we absolutely need token exchange? It does complicate compliant client implementation. |
Just thinking out loud, but perhaps having something like a This could allow the server to clean up idle connections and update security tokens if needed. Clients could implement this or be prepared to reconnect (which they should do anyway) |
Something that was was brought up in the node server security PR is giving the client some information about security requirements. We could include something in the welcome message. "authenticationRequired" = "always" | "forwrite" | "never" |
Tcp is not my forte, but isn't it so that as long as the server is sending stuff and the connection is working the client is sending back acknowledgement tcp packets. A truly idle connection may go stale, but if the server keeps sending something a client just disappering will eventually result in the terminating the connection in timeout / unreachable or something along those lines. We actually have discussed heartbeat, to be sent in absence of other messages, and it is referenced in https://github.com/SignalK/specification/blob/master/gitbook-docs/streaming_api.md#streaming-websocket-api-signalkversionstream. |
My server is stateless and does issue tokens with short validity time. That avoids having to track tokens, except as you said when logging out (hadnt considered that). Since my tokens have a short validity and auto-renew they self-expire quickly anyway. I dont call validate() at the client side, its called by the server on any incoming token, and cookie updated for REST calls, but a client could call it. There was a mention somewhere of a way for a client to find its auth status so I suggested we expose it. So in my case client auth expires unless they remain active. This fails in ws subscription as the server sends but the client just passively receives. ws is based on TCP and TCP uses FIN packet to close the connection. In the case of sudden loss of Internet connection, both the ws server and the browser are unware of the already dead TCP connection because no FIN packet was sent. To address the issue, TCP has a mechanism called keepalive, but its bound with the OS etc, and I found its not dependable for closing the ws connection in a reasonable time span (can be quite long). Given my server is async and queues messages for the client I found I end up with large queues to dead clients as I cant tell if they are dead or just slow. I know that the queue is an implementation issue but the underlying problem remains, how long do you wait for a client to accept a message before assuming they are dead. On a busy server how many such connections (resource leaks) do you tolerate? A signalk level heart-beat message over ws or other transport would solve this, effectively renewing the token, eg its basically a call to validate(), that would remove the need to expose the validate() method. (The page referenced above is a heartbeat sent by the server) |
@rob42 The only problem I see with the heartbeat is that we probably need to make it optional, since requiring it would break existing clients. I don't think this is enough to go to v2 of the spec. In v2 we could require it. Are you ok with adding info on this to the welcome message? Something like: "A server may include a |
Needs thought about ws vs http. The welcome message can just be |
I don’t see why it would be needed for http. |
Could you explain the need for the heartbeat message? What problem is it solving? How is it related to security? I just tried with the node server dropping wifi connection with an active ws connection and checked with Wireshark: the packets are retried per tcp and in about 100 seconds the connection is closed and the app is notified. Rob's anecdotal report of problems is more related to that particular implementation than the protocol we use. As long as the server is sending something the outgoing packets will timeout if the recipient can not be reached and the connection will close. Let's rely on tcp and not build mechanisms that exist in the transport layer into the application layer. |
Just read through the discussion; I have three things (cents) to add.
|
To make it easy to create webapps. The server accepts form based login credentials and sets a cookie, nothing else required from the webapp developer. |
Why do we need to be able to do in band login over ws? Ws connection authentication can use cookie and http header based authentication and login via http. I think there is value in recommending/specifying only one way for doing things, not several. Also less code in the implementation. |
I think @tkurki makes a good point here - less spec is usually better. Another upside to leaving all that out in favour for just HTTP-based login/auth is that it'll be much easier to re-use existing authentication layers both in client applications and server implementations. For client that want to request access via WS, the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
As a client developer, I like having the options. For example, if I can do everything I want to do over WS, then the user's perceived performance is much better. Now that I have login and PUT request support over WS in node server, the time from opening the app to getting data is much better. And the experience of say flipping a switch is also much better because I don't have to poll for results any more. |
Agreed, there is clear advantage in ws for all async actions, no polling needed. So how does a client discover that it should request access or provide authentication or login credentials over ws? We need to allow opening the ws without authentication to make this work. From client’s perspective the ws opens ok, there is just no data and subscriptions do not result in any response or data. Only put requests will result in responses. |
This works pretty nicely now in node server (SignalK/signalk-server#620) The client can connect to a secure server, it won't see any data. Once it either does a login or goes through the access request process, data starts flowing. I proposed above that we add something like the following to the welcome message: "authenticationRequired" = "always" | "forwrite" | "never" |
- Fixed 119 character column width - Shortened token examples to fit in code example boxes
Replaced `v1` with `«version»` in the login and logout URLs. Also, clarified some language around response codes.
I agree with "authenticationRequired" = "always" | "forwrite" | "never" in the welcome message, or in a reply to a request that cannot be fulfilled due to auth. |
This is my initial brain dump for security. It is based on the current node server implementation.
I intend to leave server details like how roles are used and defined or things like access control lists. These are server implementation details.
We should define the minimum need for a client and server to provide security
And yes, there's a lot left to do. I'm sure the list will get bigger.
TODO: