-
Notifications
You must be signed in to change notification settings - Fork 21.9k
Allows passing sub protocols with ActionCable #41415
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
Conversation
Apparently the failing builds aren't due to my changes. |
cc7c5c4
to
ba25ca4
Compare
Code rebased on the |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
Oh yeah, let's play the stupid bot game... |
@zedtux Just a random question, but is passing the token as a header legit? What would the alternatives be? For example, could it be encrypted in the session? Sorry for my random questions are not blocking this PR (it generally LGTM) but I want to understand the use-cases more. |
Thank you @zzak for your interest in my PR! Well I made it based on what it exist in all the actioncable forks without really looking deep in specs, so I can't tell now if there are alternatives.
I'm using a JWT in this case (and all actioncable forks are doing the same) which are signed by the server and therefor can't be updated from the client side, but a JWT can be enrypted for sure. If that is really required in order to get this PR merge, I can investigate, but in my use case I don't see the benefit. Just let me know. |
@zedtux Thanks for your reply. Can you point me to a couple of forks that implement the token passing part? |
@zzak please find bellow some of the existing "forks" (some are actually copies, not literally forks):
There are others but those can be a good start I think. |
Hello @zzak, Could you please give me some feedbacks? |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
Yeah well ... anyone from the Rails team to review this PR? Like @zzak, do you have some feedbacks? |
@zedtux Hey sorry for the wait, I've been limiting my activity to the newsletter and not as focused on the day-to-day stuff. This PR just needs someone with enough context to know if this is the best way to pass along the JWT for websockets, sorry I can't help more! |
Thanks for your reply. What can I do to make this happening? |
@zedtux Ok, I had some time to look into this more and came up with a few thoughts:
|
Thank you @zzak for your comment and time. Does HTTP_SEC_WEBSOCKET_PROTOCOL get filtered from logs automatically? Or can we make sure we don't log tokens somehow?In the case, by "logs", you mean "Browser logs" or "Browser console", it looks useless to me since:
Also this token is provided to the client form a server request which is then visible in the server's response. I hope I'm addressing correctly your point, but if I'm not, then please ask me more One of the libraries suggests encrypting the token, like decoded_token = JWT.decode token, Rails.application.secrets.secret_key_base, true, { :algorithm => 'HS256' }, can we do that too?Oh sure, but that's to be done on the backend side. So far the scope of this PR is to implement a clean way of passing JWTs to the server in order to authenticate the connection. Now how the JWT has been generated by the server doesn't have any impact on this PR. Even better, the token passed to the server doesn't have to be a JWT, it could be anything the backend dev would like to have. Now that being said, having security recommendations in the Rails' documentation would be definitely a nice to have. I also would like to say that some gems can help on this like the ruby-jwe gem. I'm not sure I love the name createConsumerFromMeta does it have to be an entirely new function?As Phil Karlton said:
😄 So feel free to propose a better name and I'll update it.
Well I do prefer this: ActionCable.createConsumerFromMeta("my.token.123") than this: ActionCable.createConsumer(undefined, "my.token.123") in order to make Now if no one agree on this, I can remove it, of course. |
For Re: For encrypting this value, I think it's good to have security by default -- is there any reason not to do it? This PR is not huge by any means so it may be worth adding here. |
Okay, sorry, I better understand now. So no, that's not logged in any Rails logs (I actually had to add a logging of the token while I was implementing it).
I want to use the URL from the
Encrypting the token is used full when you use it to pass data from the server to the client, but when you use an empty token, but signed, to authenticate the connexion (like I'm doing in my app), encrypting it is not an issue, but some resources usage for nothing in my opinion. That means it has to be optional. Now I have questions:
|
@zzak can you please have a look at my reply? |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
Oh no ... 😕, not that stupid bots ... @zzak can we continue on this PR please or can you please mention the right person to review it ? 🙏 |
Can we move on with this PR please ? I'm ready to answer/address all the questions, like I did with @zzak. I really would like to see this PR merged 🙏. |
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.
Hey @zedtux, thanks for working on this. I made some suggestions, but I think this makes a lot of sense 👍.
I see you have gone through some back and forth here with the PR getting auto-closed several times. I'll promise I'll get this merged after those changes :).
@@ -28,8 +28,9 @@ import Subscriptions from "./subscriptions" | |||
// automatically resubscribe. | |||
|
|||
export default class Consumer { | |||
constructor(url) { | |||
constructor(url, token = null) { |
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.
I would leave the existing constructor as it is and, instead, add a new method to add subprotocols. It can be as simple as:
consumer = createConsumer("https://ws.example.com/cable")
consumer.addSubProtocol("some subprotocol")
Then, the consumer can expose a list of consumer.subProtocols
to use when initializing the WebSocket connection.
I'd use the name sub protocol instead of token to make it clear what this is doing. Using the sub protocol as a way to transport a JWT token is what motivates this, but we are adding a broader functionality here.
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.
@zedtux also, we should document the change in actioncable/CHANGELOG.md
(you can see the format in other frameworks such as activerecord/CHANGELOG.md
, since right now it's empty for Action Cable).
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.
Thank you very much for your comment 🙏 I will adapts as requested by tomorrow.
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.
A quick update : I've already started to update my code, but I have to finish a milestone first. I'll come back on this on asap.
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.
I'm done with the milestone, I will be able to work on this very soon !
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.
I'm on it !
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.
@jorgemanrubia I'm done. I have spent quite some time looking to assert the HTTP header content in order to check the subprotocols sent by WebSocket but it doesn't seem to be supported by mock-socket.
Let me know please if you see additional test to be added.
1b31959
to
67d5b6a
Compare
67d5b6a
to
6edb465
Compare
@jorgemanrubia could you please have a look a this PR ? |
Hello @zzak and @jorgemanrubia, First of all, happy new year ! :) What about this PR ? Could you please review it / test it / merge it ? Is there anything else I should do ? Thanks |
@zedtux Hey sorry for the wait, I just re-read this thread and have some thoughts. RE: createConsumer(undefined, token) vs. createConsumerFromMeta
Is it safe to assume that everyone will want that behavior? It seems like we are hiding the URL attribute in Also, now that I'm looking at the diff it seems RE: Encryption As I understand it, this has to happen on the Rails backend and that this PR is simply adding the ability to pass data in this header to the client. Is that correct? IMO, we can talk about the backend part after but I think if we're going to add this capability to actioncable, we should consider what the full feature looks like. Does that make sense?
I'm not 100% sure I follow, and I don't want to make any false assumptions, so I wonder if it's better if we had some examples of different use-cases.
I think it could be optional, but yes, this is what I mean by the full picture.
Documentation is another option, we can simply explain how to use the feature properly, but I feel "The Rails Way" is to provide sane defaults and prevent the user from ...going off the rails. 😂
I think that goes along with the previous question before docs, if we're going to implement this properly we should consider the whole stack, not just actioncable IMO. |
@zzak thank you for your comment. I think you missed the exchanges with @jorgemanrubia here where he asked me to revert the He also told me :
As you said :
To be a little bit more precise, it adds the ability to pass sub protocols from the client to the server. You are raising good points, but I don't think it's the right place to discuss them, and I'm even not sure to be the right person to answer your questions. I did the changes, the build is green, I don't see much I can do to get this PR merged now. Can anyone please just merge this PR ? |
@zedtux Thanks for the explanation, and appreciate your patience with me. Now that I understand what you mean, I think this PR is good to go. Could you please update the title and description, I think we don't need to specify Thanks! |
@zzak could you please review the PR title and description and let me know if it's fine like that ? |
Thank you very much all ! 🎉 |
TODO: Change to use main rails after rails/rails#41415 available
Summary
The goal of this PR is to allow passing sub protocols to the backend via ActionCable.
Usage
which will sends the following protocol list :
[ "actioncable-v1-json", "actioncable-unsupported", "custom-protocol" ]