anycable / anycable-rails

AnyCable for Ruby on Rails applications
https://anycable.io
MIT License
499 stars 35 forks source link

AnyCable::Rails::Rack.middleware.use problems with session, seeing only session_id #156

Closed alekseyl closed 3 years ago

alekseyl commented 3 years ago

Tell us about your environment

Ruby version: 2.6

Rails version: 6.0.1

anycable gem version: (1.0.3)

anycable-rails gem version: (1.0.7)

grpc gem version: (1.36.0)

What did you do?

I tried to use an authenticate solution alternative to the Devise gem:

AnyCable::Rails::Rack.middleware.use  RailsWarden::Manager do |manager|
  manager.failure_app = Proc.new { |_env|
    [401, {'Content-Type' => 'application/json'}, { errors: { messages: 'You need to sign in or sign up before continuing.' } }.to_json]
  }
  manager.default_strategies [:password] # needs to be defined
  # Optional Settings (see Warden wiki)
  # manager.scope_defaults :admin, strategies: [:password]
  manager.default_scope = :user # optional default scope, otherwise 'default' will be used!
  # manager.intercept_401 = false # Warden will intercept 401 responses, which can cause conflicts
end

This configuration was copy/pasted from a Rails middlewares config.

After noticing that session is not deserialized properly I checked the whole list of rails middlewares, and selected two middlewares related to cookies in an original default rails order:

use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore

Which weren't present at the AnycableRails middlewares list. So I added them in the same exact order to AnycableRails middlewares.

What did you expect to happen?

My expectations were to have a nice and shiny sessions de-serialization same as I do have on my controllers, but nope, expectations were failed. Instead of session be like:

web_1 | 13:04:44 web.1 | { web_1 | 13:04:44 web.1 | "session_id" => "187bcfc0d913878b60e63eea115822c1", web_1 | 13:04:44 web.1 | "warden.user.user.key" => [ web_1 | 13:04:44 web.1 | [0] "User", web_1 | 13:04:44 web.1 | [1] 1 web_1 | 13:04:44 web.1 | ], web_1 | 13:04:44 web.1 | "_csrf_token" => "W5H2yYdPbMscV68nJbZf2l0jrrYuT++AGvpuJZxOoEk=" web_1 | 13:04:44 web.1 | }

output was:

rpc_1 | { rpc_1 | "session_id" => "3ecd9a95b397deae16cf5ea2d34764ff" rpc_1 | }

It wasn't the cookies problem, cause sending them in a raw header via curl deliver correct session deserialization in controller. So rpc got the cookies header right, and the problem was around some middlewares misalignment.

Solution

By the end of the day solution was found: do not use ActionDispatch::Session::CookieStore.

Configuration of AnycableRails.middleware in case you are not using the devise gem, should use any of: Rack::Session::Cookie OR ActionDispatch::Cookies.

AND DO NOT use the ActionDispatch::Session::CookieStore middleware.

Since I was already using rails I fell for this final configuration for AnycableRails custom middlewares:

AnyCable::Rails::Rack.middleware.use ActionDispatch::Cookies
# OR Rack::Session::Cookie
AnyCable::Rails::Rack.middleware.use Warden::Manager do ....
palkan commented 3 years ago

Thanks! Added to docs.

Ranger-X commented 3 years ago

You also need to take into account that sometimes you need to change Devise's default_scope like this:

  AnyCable::Rails::Rack.middleware.use Warden::Manager do |config|
    config.default_scope = :user
    Devise.warden_config = config
  end