Linger
Linger takes advantage of the new throw :forbidden
feature in StimulusReflex 3.5 to provide unopinionated authentication security to Reflexes. If you have two tabs open and sign out in one, you should not be able to run Reflexes in the other tab. If you have sessions active on multiple devices, signing out on one should not impact the sessions on the others. These deceptively hard requirements are addressed by creating a composite key in Redis for every uniquely identified session.
Developers can respond to authentication failures by handling Reflexes that arrive in a new forbidden
state. Forbidden Reflexes are functionally identical to halted Reflexes (eg. throw :abort
) except that they (conceptually and semantically) represent Reflexes which were not allowed to execute.
You are strongly advised to consult the Lingering Presence reference application for further details, especially in advance of full documentation.
Requirements and setup
- StimulusReflex 3.5
- Redis server 4.0 and Redis Ruby client 4.2
Add the linger
gem to your Gemfile
.
Linger makes use of the same shared Redis config that Kredis uses. Just make sure that you have a valid config/redis/shared.yml
and you're all set. You can learn more on the Kredis page.
Usage
In your Reflex class, create a before_reflex
callback:
before_reflex :authenticate_connection!
We recommend placing this in the app/reflexes/application_reflex.rb
class, but you can manually add it to your Reflex classes on an ad-hoc basis.
class ApplicationReflex < StimulusReflex::Reflex
before_reflex :authenticate_connection!
end
You are then responsible for calling Linger
methods to allow and deny user contexts across all possible cases. You pass in whatever variables are expected, based on your identified_by
Set that is defined in your app/channels/application_channel/channel.rb
. The order you provide the identifiers is not important:
# after sign in
Linger.allow session, current_user
Linger.deny session
# after sign out
Linger.allow session
Linger.deny session, current_user
Finally, the developer can either terminate Action Cable connections or create a handler for the reflexForbidden
life-cycle state. You can see that a starting point for customization is presented in app/javascript/controllers/application_controller.js
where you can see a sample handler for forbidden Reflexes is described. Instead of refreshing the page, you could use a notification library to pop up a toast message, for example.
Credit and acknowledgements
The structure of this project borrows both layout and code extensively from Kredis. Thanks to all of the Kredis contributors for such an excellent tool.