Can't logout with devise
diegobernardes opened this issue ยท 7 comments
I don't have any problem with the login, it works really well. But the logout raises an exception. Any idea on how to solve this?
This just happens if I set the config.require_tenant = true
, with config.require_tenant = false
I don't have any problem.
If I set this at the application_controller.rb
and set the config.require_tenant = false
:
before_action do
puts "tenant: #{current_tenant.subdomain}"
end
All the requests print tenant: account
, even the devise session destroy that previously was causing an exception.
Log with config.require_tenant = true
:
Started POST "/users/sign_in" for 127.0.0.1 at 2020-12-21 18:01:41 +0000
Processing by Devise::SessionsController#create as HTML
Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"admin@admin.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}
Account Load (0.7ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."subdomain" = $1 ORDER BY "accounts"."id" ASC LIMIT $2 [["subdomain", "account"], ["LIMIT", 1]]
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."account_id" = $1 AND "users"."email" = $2 ORDER BY "users"."id" ASC LIMIT $3 [["account_id", 1], ["email", "admin@admin.com"], ["LIMIT", 1]]
TRANSACTION (0.6ms) BEGIN
User Update (0.7ms) UPDATE "users" SET "sign_in_count" = $1, "current_sign_in_at" = $2, "updated_at" = $3 WHERE "users"."id" = $4 [["sign_in_count", 2], ["current_sign_in_at", "2020-12-21 18:01:41.349496"], ["updated_at", "2020-12-21 18:01:41.349840"], ["id", 1]]
TRANSACTION (3.7ms) COMMIT
Redirected to http://account.lvh.me:3000/
Completed 302 Found in 208ms (ActiveRecord: 6.4ms | Allocations: 8111)
Started GET "/" for 127.0.0.1 at 2020-12-21 18:01:41 +0000
Processing by DashboardController#index as HTML
Account Load (0.5ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."subdomain" = $1 ORDER BY "accounts"."id" ASC LIMIT $2 [["subdomain", "account"], ["LIMIT", 1]]
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."account_id" = $1 AND "users"."id" = $2 ORDER BY "users"."id" ASC LIMIT $3 [["account_id", 1], ["id", 1], ["LIMIT", 1]]
Rendering layout layouts/application.html.erb
Rendering dashboard/index.html.erb within layouts/application
Rendered dashboard/index.html.erb within layouts/application (Duration: 0.2ms | Allocations: 76)
[Webpacker] Everything's up-to-date. Nothing to do
Rendered layout layouts/application.html.erb (Duration: 20.1ms | Allocations: 4125)
Completed 200 OK in 25ms (Views: 20.5ms | ActiveRecord: 1.1ms | Allocations: 6913)
Started DELETE "/users/sign_out" for 127.0.0.1 at 2020-12-21 18:01:46 +0000
Processing by Devise::SessionsController#destroy as HTML
Parameters: {"authenticity_token"=>"[FILTERED]"}
Completed 500 Internal Server Error in 1ms (ActiveRecord: 0.0ms | Allocations: 704)
ActsAsTenant::Errors::NoTenantSet (ActsAsTenant::Errors::NoTenantSet):
acts_as_tenant (0.5.0) lib/acts_as_tenant/model_extensions.rb:20:in `block in acts_as_tenant'
activerecord (6.1.0) lib/active_record/scoping/default.rb:120:in `instance_exec'
activerecord (6.1.0) lib/active_record/scoping/default.rb:120:in `block (2 levels) in build_default_scope'
activerecord (6.1.0) lib/active_record/scoping/default.rb:118:in `each'
activerecord (6.1.0) lib/active_record/scoping/default.rb:118:in `inject'
activerecord (6.1.0) lib/active_record/scoping/default.rb:118:in `block in build_default_scope'
activerecord (6.1.0) lib/active_record/scoping/default.rb:142:in `evaluate_default_scope'
activerecord (6.1.0) lib/active_record/scoping/default.rb:117:in `build_default_scope'
activerecord (6.1.0) lib/active_record/scoping/named.rb:46:in `default_scoped'
activerecord (6.1.0) lib/active_record/scoping/named.rb:32:in `all'
activerecord (6.1.0) lib/active_record/querying.rb:22:in `where'
orm_adapter (0.5.0) lib/orm_adapter/adapters/active_record.rb:17:in `get'
devise (4.7.3) lib/devise/models/authenticatable.rb:238:in `serialize_from_session'
devise (4.7.3) lib/devise.rb:481:in `block (2 levels) in configure_warden!'
warden (1.2.9) lib/warden/session_serializer.rb:35:in `fetch'
warden (1.2.9) lib/warden/proxy.rb:224:in `user'
devise (4.7.3) app/controllers/devise/sessions_controller.rb:70:in `block in all_signed_out?'
devise (4.7.3) app/controllers/devise/sessions_controller.rb:70:in `map'
devise (4.7.3) app/controllers/devise/sessions_controller.rb:70:in `all_signed_out?'
devise (4.7.3) app/controllers/devise/sessions_controller.rb:62:in `verify_signed_out_user'
activesupport (6.1.0) lib/active_support/callbacks.rb:427:in `block in make_lambda'
activesupport (6.1.0) lib/active_support/callbacks.rb:179:in `block (2 levels) in halting_and_conditional'
actionpack (6.1.0) lib/abstract_controller/callbacks.rb:34:in `block (2 levels) in <module:Callbacks>'
activesupport (6.1.0) lib/active_support/callbacks.rb:180:in `block in halting_and_conditional'
activesupport (6.1.0) lib/active_support/callbacks.rb:512:in `block in invoke_before'
activesupport (6.1.0) lib/active_support/callbacks.rb:512:in `each'
activesupport (6.1.0) lib/active_support/callbacks.rb:512:in `invoke_before'
activesupport (6.1.0) lib/active_support/callbacks.rb:115:in `block in run_callbacks'
activesupport (6.1.0) lib/active_support/callbacks.rb:137:in `run_callbacks'
actionpack (6.1.0) lib/abstract_controller/callbacks.rb:41:in `process_action'
actionpack (6.1.0) lib/action_controller/metal/rescue.rb:22:in `process_action'
actionpack (6.1.0) lib/action_controller/metal/instrumentation.rb:34:in `block in process_action'
activesupport (6.1.0) lib/active_support/notifications.rb:203:in `block in instrument'
activesupport (6.1.0) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport (6.1.0) lib/active_support/notifications.rb:203:in `instrument'
actionpack (6.1.0) lib/action_controller/metal/instrumentation.rb:33:in `process_action'
actionpack (6.1.0) lib/action_controller/metal/params_wrapper.rb:249:in `process_action'
activerecord (6.1.0) lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack (6.1.0) lib/abstract_controller/base.rb:165:in `process'
actionview (6.1.0) lib/action_view/rendering.rb:39:in `process'
actionpack (6.1.0) lib/action_controller/metal.rb:190:in `dispatch'
actionpack (6.1.0) lib/action_controller/metal.rb:254:in `dispatch'
actionpack (6.1.0) lib/action_dispatch/routing/route_set.rb:50:in `dispatch'
actionpack (6.1.0) lib/action_dispatch/routing/route_set.rb:33:in `serve'
actionpack (6.1.0) lib/action_dispatch/routing/mapper.rb:19:in `block in <class:Constraints>'
actionpack (6.1.0) lib/action_dispatch/routing/mapper.rb:49:in `serve'
actionpack (6.1.0) lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack (6.1.0) lib/action_dispatch/journey/router.rb:32:in `each'
actionpack (6.1.0) lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack (6.1.0) lib/action_dispatch/routing/route_set.rb:842:in `call'
warden (1.2.9) lib/warden/manager.rb:36:in `block in call'
warden (1.2.9) lib/warden/manager.rb:34:in `catch'
warden (1.2.9) lib/warden/manager.rb:34:in `call'
rack (2.2.3) lib/rack/tempfile_reaper.rb:15:in `call'
rack (2.2.3) lib/rack/etag.rb:27:in `call'
rack (2.2.3) lib/rack/conditional_get.rb:40:in `call'
rack (2.2.3) lib/rack/head.rb:12:in `call'
actionpack (6.1.0) lib/action_dispatch/http/permissions_policy.rb:22:in `call'
actionpack (6.1.0) lib/action_dispatch/http/content_security_policy.rb:18:in `call'
rack (2.2.3) lib/rack/session/abstract/id.rb:266:in `context'
rack (2.2.3) lib/rack/session/abstract/id.rb:260:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/cookies.rb:689:in `call'
activerecord (6.1.0) lib/active_record/migration.rb:601:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport (6.1.0) lib/active_support/callbacks.rb:98:in `run_callbacks'
actionpack (6.1.0) lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/actionable_exceptions.rb:18:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/debug_exceptions.rb:29:in `call'
web-console (4.1.0) lib/web_console/middleware.rb:132:in `call_app'
web-console (4.1.0) lib/web_console/middleware.rb:28:in `block in call'
web-console (4.1.0) lib/web_console/middleware.rb:17:in `catch'
web-console (4.1.0) lib/web_console/middleware.rb:17:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
railties (6.1.0) lib/rails/rack/logger.rb:37:in `call_app'
railties (6.1.0) lib/rails/rack/logger.rb:26:in `block in call'
activesupport (6.1.0) lib/active_support/tagged_logging.rb:99:in `block in tagged'
activesupport (6.1.0) lib/active_support/tagged_logging.rb:37:in `tagged'
activesupport (6.1.0) lib/active_support/tagged_logging.rb:99:in `tagged'
railties (6.1.0) lib/rails/rack/logger.rb:26:in `call'
sprockets-rails (3.2.2) lib/sprockets/rails/quiet_assets.rb:13:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
request_store (1.5.0) lib/request_store/middleware.rb:19:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/request_id.rb:26:in `call'
rack (2.2.3) lib/rack/method_override.rb:24:in `call'
rack (2.2.3) lib/rack/runtime.rb:22:in `call'
activesupport (6.1.0) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/static.rb:24:in `call'
rack (2.2.3) lib/rack/sendfile.rb:110:in `call'
actionpack (6.1.0) lib/action_dispatch/middleware/host_authorization.rb:92:in `call'
rack-mini-profiler (2.2.0) lib/mini_profiler/profiler.rb:368:in `call'
webpacker (5.2.1) lib/webpacker/dev_server_proxy.rb:25:in `perform_request'
rack-proxy (0.6.5) lib/rack/proxy.rb:57:in `call'
railties (6.1.0) lib/rails/engine.rb:539:in `call'
puma (5.1.1) lib/puma/configuration.rb:246:in `call'
puma (5.1.1) lib/puma/request.rb:76:in `block in handle_request'
puma (5.1.1) lib/puma/thread_pool.rb:337:in `with_force_shutdown'
puma (5.1.1) lib/puma/request.rb:75:in `handle_request'
puma (5.1.1) lib/puma/server.rb:431:in `process_client'
puma (5.1.1) lib/puma/thread_pool.rb:145:in `block in spawn_thread'
This is the output when the logout is done with success using config.require_tenant = false
:
Started DELETE "/users/sign_out" for 127.0.0.1 at 2020-12-21 18:12:21 +0000
Processing by Devise::SessionsController#destroy as HTML
Parameters: {"authenticity_token"=>"[FILTERED]"}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Redirected to http://account.lvh.me:3000/
Completed 302 Found in 7ms (ActiveRecord: 1.4ms | Allocations: 3338)
So devise is doing a user query without the tenant. There is any way to solve this problem?
Sounds like you're not setting the tenant before the Devise controller action runs. How are you setting the tenant?
My application controller looks like this:
class ApplicationController < ActionController::Base
set_current_tenant_by_subdomain(:account, :subdomain)
before_action do
puts "tenant: #{current_tenant.subdomain}"
end
end
And I'm accessing the app like this: http://account.lvh.me:3000
.
This is the execution log with the require_tenant = false
and the before action printing the current tenant. As you can see the tenant is set at the devise session controller but it's not used at the actual user query. Maybe devise overriding acts_as_tenant? Maybe the tenant is being set after the session controller executes?
=> Booting Puma
=> Rails 6.1.0 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.1.1 (ruby 2.7.2-p137) ("At Your Service")
* Min threads: 5
* Max threads: 5
* Environment: development
* PID: 66238
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop
Started GET "/" for 127.0.0.1 at 2020-12-21 18:21:26 +0000
(3.5ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Processing by DashboardController#index as HTML
Account Load (8.1ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."subdomain" = $1 ORDER BY "accounts"."id" ASC LIMIT $2 [["subdomain", "account"], ["LIMIT", 1]]
tenant: account
Completed 401 Unauthorized in 36ms (ActiveRecord: 21.2ms | Allocations: 5826)
Started GET "/users/sign_in" for 127.0.0.1 at 2020-12-21 18:21:26 +0000
Processing by Devise::SessionsController#new as HTML
Account Load (0.6ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."subdomain" = $1 ORDER BY "accounts"."id" ASC LIMIT $2 [["subdomain", "account"], ["LIMIT", 1]]
tenant: account
Rendering layout layouts/application.html.erb
Rendering /Users/dbernardes/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/devise-4.7.3/app/views/devise/sessions/new.html.erb within layouts/application
Rendered /Users/dbernardes/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/devise-4.7.3/app/views/devise/shared/_links.html.erb (Duration: 1.2ms | Allocations: 540)
Rendered /Users/dbernardes/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/devise-4.7.3/app/views/devise/sessions/new.html.erb within layouts/application (Duration: 7.9ms | Allocations: 2830)
[Webpacker] Everything's up-to-date. Nothing to do
Rendered layout layouts/application.html.erb (Duration: 37.5ms | Allocations: 8968)
Completed 200 OK in 59ms (Views: 41.6ms | ActiveRecord: 3.8ms | Allocations: 19883)
Started POST "/users/sign_in" for 127.0.0.1 at 2020-12-21 18:21:35 +0000
Processing by Devise::SessionsController#create as HTML
Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"admin@admin.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}
Account Load (0.8ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."subdomain" = $1 ORDER BY "accounts"."id" ASC LIMIT $2 [["subdomain", "account"], ["LIMIT", 1]]
tenant: account
User Load (11.2ms) SELECT "users".* FROM "users" WHERE "users"."account_id" = $1 AND "users"."email" = $2 ORDER BY "users"."id" ASC LIMIT $3 [["account_id", 1], ["email", "admin@admin.com"], ["LIMIT", 1]]
TRANSACTION (0.7ms) BEGIN
User Update (0.8ms) UPDATE "users" SET "sign_in_count" = $1, "current_sign_in_at" = $2, "last_sign_in_at" = $3, "updated_at" = $4 WHERE "users"."id" = $5 [["sign_in_count", 7], ["current_sign_in_at", "2020-12-21 18:21:35.478149"], ["last_sign_in_at", "2020-12-21 18:12:16.581563"], ["updated_at", "2020-12-21 18:21:35.478517"], ["id", 1]]
TRANSACTION (2.7ms) COMMIT
Redirected to http://account.lvh.me:3000/
Completed 302 Found in 221ms (ActiveRecord: 16.2ms | Allocations: 9155)
Started GET "/" for 127.0.0.1 at 2020-12-21 18:21:35 +0000
Processing by DashboardController#index as HTML
Account Load (0.6ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."subdomain" = $1 ORDER BY "accounts"."id" ASC LIMIT $2 [["subdomain", "account"], ["LIMIT", 1]]
tenant: account
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."account_id" = $1 AND "users"."id" = $2 ORDER BY "users"."id" ASC LIMIT $3 [["account_id", 1], ["id", 1], ["LIMIT", 1]]
Rendering layout layouts/application.html.erb
Rendering dashboard/index.html.erb within layouts/application
Rendered dashboard/index.html.erb within layouts/application (Duration: 0.8ms | Allocations: 161)
[Webpacker] Everything's up-to-date. Nothing to do
Rendered layout layouts/application.html.erb (Duration: 16.6ms | Allocations: 4214)
Completed 200 OK in 23ms (Views: 18.0ms | ActiveRecord: 1.3ms | Allocations: 7572)
Started DELETE "/users/sign_out" for 127.0.0.1 at 2020-12-21 18:21:37 +0000
Processing by Devise::SessionsController#destroy as HTML
Parameters: {"authenticity_token"=>"[FILTERED]"}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Account Load (0.7ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."subdomain" = $1 ORDER BY "accounts"."id" ASC LIMIT $2 [["subdomain", "account"], ["LIMIT", 1]]
tenant: account
Redirected to http://account.lvh.me:3000/
Completed 302 Found in 10ms (ActiveRecord: 1.5ms | Allocations: 3360)
Started GET "/" for 127.0.0.1 at 2020-12-21 18:21:37 +0000
Processing by DashboardController#index as HTML
Account Load (0.9ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."subdomain" = $1 ORDER BY "accounts"."id" ASC LIMIT $2 [["subdomain", "account"], ["LIMIT", 1]]
tenant: account
Completed 401 Unauthorized in 4ms (ActiveRecord: 0.9ms | Allocations: 1425)
Started GET "/users/sign_in" for 127.0.0.1 at 2020-12-21 18:21:37 +0000
Processing by Devise::SessionsController#new as HTML
Account Load (0.7ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."subdomain" = $1 ORDER BY "accounts"."id" ASC LIMIT $2 [["subdomain", "account"], ["LIMIT", 1]]
tenant: account
Rendering layout layouts/application.html.erb
Rendering /Users/dbernardes/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/devise-4.7.3/app/views/devise/sessions/new.html.erb within layouts/application
Rendered /Users/dbernardes/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/devise-4.7.3/app/views/devise/shared/_links.html.erb (Duration: 0.1ms | Allocations: 108)
Rendered /Users/dbernardes/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/devise-4.7.3/app/views/devise/sessions/new.html.erb within layouts/application (Duration: 1.5ms | Allocations: 984)
[Webpacker] Everything's up-to-date. Nothing to do
Rendered layout layouts/application.html.erb (Duration: 19.2ms | Allocations: 5120)
Completed 200 OK in 23ms (Views: 19.6ms | ActiveRecord: 0.7ms | Allocations: 6991)
Thanks!
Looks like Devise prepends all their before_actions. https://github.com/heartcombo/devise/blob/master/app/controllers/devise/sessions_controller.rb#L4-L7
And according to your logs, verify_signed_out_user
was the one that looked up the User.
I guess you'll have to use prepend_before_action
for the devise_controller.
Try adding this to ApplicationController
:
prepend_before_action :find_tenant_by_subdomain if devise_controller?
At the applicaton_controller.rb
the prepend didn't solve the problem. But I was able to fix the problem by creating a new controller with the prepend and updating the routes:
# controller
class SessionsController < Devise::SessionsController
prepend_before_action :find_tenant_by_subdomain
end
# routes
devise_for :users, controllers: {sessions: "sessions"}
As devise is one of the most used gems with rails I think it's worth adding some notes about this problem even not being caused by acts_as_tenant.
And thanks for the help @excid3!
I know this is an old issue but I am having the same issue. Sadly none of the solutions worked when I tried them. I think this is because I am also using devise_scope in my routes. I have come up with a working solutions and was interested in some feedback.
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
devise_for :users
devise_scope :user do
# Defines the root path route ("/") for authenticated users
authenticated :user do
root 'dashboard#index', as: :authenticated_root
end
# Defines the root path route ("/") for unauthenticated users
unauthenticated do
root 'devise/sessions#new', as: :unauthenticated_root
end
end
end
I created a small piece of middleware in lib/acts_as_tenant/middleware.rb
module ActsAsTenant
module Middleware
class Subdomain
def initialize(app)
@app = app
end
def call(env)
request = ActionDispatch::Request.new(env)
if (subdomain = request.subdomains.send(:last))
ActsAsTenant.current_tenant = Account.where(subdomain: subdomain.downcase).first
end
@app.call env
end
end
end
end
In config/application.rb
require the middleware and add it to the config.
require_relative "boot"
require "rails/all"
require_relative '../lib/acts_as_tenant/middleware'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module blog
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
config.middleware.use ActsAsTenant::Middleware::Subdomain
end
end
In the application controller I catch any unknown subdomains/tenants and display the 404.html -> I will probably change this to something else in the future.
class ApplicationController < ActionController::Base
rescue_from ActsAsTenant::Errors::NoTenantSet, with: :no_tenant_set
before_action :authenticate_user!
def no_tenant_set
render file: "#{Rails.root}/public/404.html", layout: false
end
end
I would appreciate any thoughts or suggestions :)
I had today the same issue with Devise log in (but not on log out!) on a new Rails 7 application, where we are using an ActiveSupport::Concern
in ApplicationController
to set up set_current_tenant_through_filter
with before_action :set_tenant
.
Changing it to prepend_before_action
does not help. We added a SessionsController
as suggested in #250 (comment) and now it works. Thanks!