-
Notifications
You must be signed in to change notification settings - Fork 329
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow the application to configure Turbo::StreamChannel’s inheritance
`ApplicationCable::Connection` provides a simple and intuitive way to authenticate both custom ActionCable Channels and the `Turbo::Broadcastable` broadcasts made on `Turbo::StreamsChannel`. In multi-tenancy applications simply authenticating the user is often not enough as an evicted user could subscribe while being subscribed to another account. This PR allows an application to configure `Turbo::StreamsChannel`’s superclass with the intention of allowing application specific authorization logic to be implemented in e.g `ApplicationCable::Channel`. `ApplicationCable::Channel` is generated by rails (new and channel) but could have been removed. It’s also possible that `ApplicationCable::Channel` implements `authorized?` in a way that’s not meant to be used by `Turbo::StreamsChannel`. To avoid this from being a breaking change `Turbo::StreamsChannel`’s super class defaults to `ActionCable::Channel::Base` (the current behavior). User can opt-in to inheriting from `ApplicationCable::Channel` (or any other Channel) with: ```rb config.turbo.base_stream_channel_class = "ApplicationCable::Channel" ``` Example of attacks possible in a multi-tenancy application when a Channel is streaming from a `Turbo::Broadcastable` compatible stream name: 1. A user uses the browser’s “Save as…” and sends the html file to a colleague or is tricked to send it directly to a bad actor. Since `turbo_stream_from` makes the signed stream name appear in the HTML the HTML needs to be treated as a secret similar to if a password or API token was present. 2. Someone with access to a shared stream can save the signed stream name. If the user is later removed from the account with the expectation to loose access to the shared stream they can sign up for another account and stream from there even with an authentication check in place. Some reasons why attacks are more likely / more severe. 1. Stealing a signed token name is could be more practical, easier and/or faster than e.g stealing cookies. 2. Web Socket connection and subscription logs might not be as well tracked as HTTP logs leading to breaches being harder to discover. 3. Less is published on Web Socket security than HTTP and awareness of the need to authenticate and authorize Web Sockets may be lower than that of HTTP. 4. Signed Stream Names never expire and could go unnoticed for a long time. Leaking an old stream name doesn’t have the same protection as e.g leaking an old password reset token has. I believe we should move towards making this the default as a way to encourage securing `Turbo::Broadcastable` broadcasts in the following steps: 1. This PR. No encouragements but a cleaner way and documented way to implement authorization. 2. Generate new rails applications with: `config.turbo.base_stream_channel_class = "ApplicationCable::Channel"` and include a commented-out authentication example in the `ApplicationCable::Connection` template and a commented-out authorization example in `ApplicationCable::Channel` template. 4. Change the default value of `base_stream_channel_class` to `"ApplicationCable::Channel"` and update rails new to generate the opposite for those wishing to opt-out. `config.turbo.base_stream_channel_class = "ActionCable::Channel::Base"` 5. Remove the configuration and hard code `ApplicationCable::Channel` as `Turbo::StreamsChannel`’s super class.
- Loading branch information
Showing
7 changed files
with
188 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# When streaming from a model instance using <tt>turbo_stream_from @post</tt>, it can be useful to locate the instance | ||
# in <tt>config.turbo.base_stream_channel_class</tt>. These helper methods are available as a convenience for applications | ||
# to implement custom logic such as authorization. | ||
module Turbo::Streams::LocatableName | ||
# Locate a single streamable. Useful when subscribing with <tt>turbo_stream_from @post</tt>. It can be used e.g to | ||
# implement application-specific authorization, ex: <tt>current_user.can_access? locate_streamable</tt> | ||
def locate_streamable | ||
@locate_streamable ||= GlobalID::Locator.locate(verified_stream_name_from_params) | ||
end | ||
|
||
# Locate multiple streamables. Useful when subscribing with <tt>turbo_stream_from @post1, @post2</tt>. It can be | ||
# used e.g to implement application-specific authorization, ex: | ||
# <tt>locate_streamables.present? && locate_streamables.all? { |streamable| current_user.can_access?(streamable) }</tt> | ||
def locate_streamables | ||
@locate_streamables ||= GlobalID::Locator.locate_many(verified_stream_name_parts_from_params) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters