title: Devise and Multi-tenant Scoping
body: <p>NOTE: The original question has changed a bit. I have found two solutions, and probably on the way to change the design altogether.</p>
  
  <p>In any case, I would be interested to know, why RequestStore is not working (is it because Warden intercepts the message ahead in the middleware stack?), how Thread.current is working, and why instance variable is an unstable solution.</p>
  
  <hr>
  
  <p>I have enabled multi-tenancy using default_scope in my application, including the Devise User model.</p>
  
  <p>In the application_controller.rb, I have</p>
  
  <pre><code>around_filter :set_request_store
  
  def set_request_store
    Tenant.current = current_tenant.id
    yield
  ensure
    Tenant.current = nil
  end
  </code></pre>
  
  <p>And Tenant.current in turn sets a RequestStore hash key.</p>
  
  <p>In tenant.rb</p>
  
  <pre><code>def self.current
    RequestStore.store[:current_tenant_id]
  end
  
  def self.current=(tenant_id)
    RequestStore.store[:current_tenant_id] = tenant_id
  end
  </code></pre>
  
  <p>In my routes.rb file, I have the following</p>
  
  <pre><code>  unauthenticated do
      root to: 'home#index', as: :public_root
    end
  
    authenticated :user do
      root to: 'dashboard#index', as: :application_root
    end
  </code></pre>
  
  <p>The problem I faced is better illustrated through the logs.</p>
  
  <p>After a successful sign in.</p>
  
  <pre><code>Started POST "/users/sign_in" for 127.0.0.1 at 2014-09-24 14:57:13 +0530
  Processing by Devise::SessionsController#create as HTML
    Parameters: {"utf8"=&gt;"✓", "authenticity_token"=&gt;"[FILTERED]", "user"=&gt;{"tenant_id"=&gt;"1", "email"=&gt;"user@example.com", "password"=&gt;"[FILTERED]"}}
    Tenant Load (0.9ms)  SELECT  "tenants".* FROM "tenants"  WHERE "tenants"."subdomain" = 'test'  ORDER BY "tenants"."id" ASC LIMIT 1
    User Load (0.8ms)  SELECT  "users".* FROM "users"  WHERE "users"."tenant_id" = 1 AND "users"."email" = 'user@example.com'  ORDER BY "users"."id" ASC LIMIT 1
     (0.2ms)  BEGIN
    SQL (0.5ms)  UPDATE "users" SET "current_sign_in_at" = $1, "last_sign_in_at" = $2, "sign_in_count" = $3, "updated_at" = $4 WHERE "users"."id" = 1  [["current_sign_in_at", "2014-09-24 09:27:13.553818"], ["last_sign_in_at", "2014-09-24 09:26:31.548568"], ["sign_in_count", 44], ["updated_at", "2014-09-24 09:27:13.556155"]]
     (1.1ms)  COMMIT
  </code></pre>
  
  <p>Devise redirects the application to (application) root path. Effectively, the path is the same for public and application roots.</p>
  
  <pre><code>Redirected to http://test.com.dev/
  Completed 302 Found in 90ms (ActiveRecord: 3.4ms)
  </code></pre>
  
  <p>The unauthenticated method call in routes is (probably) trying to authenticate the user (using Warden somewhere in the middleware???) and the tenant_id is not set at that point. see the WHERE clause for tenant_id.</p>
  
  <pre><code>Started GET "/" for 127.0.0.1 at 2014-09-24 14:57:13 +0530
    User Load (0.8ms)  SELECT  "users".* FROM "users"  WHERE "users"."tenant_id" IS NULL AND "users"."id" = 1  ORDER BY "users"."id" ASC LIMIT 1
  Processing by HomeController#index as HTML
  </code></pre>
  
  <p>Has anyone faced such an issue and solved it?</p>
  
  <hr>
  
  <p><strong>SOLUTION 1:</strong></p>
  
  <p>First, I have solved it using Thread.current. For some reason RequestStore.store is not getting set inside Devise methods.</p>
  
  <p>The following code solves the login issue. However, I could not find a place to safely unset the value on Thread.current.</p>
  
  <p>In user.rb</p>
  
  <pre><code>devise ...,
         request_keys: [:subdomain]
  
  
  default_scope { where(tenant_id: (Tenant.current || Thread.current[:current_tenant_id])) }
  
  protected
  
  def self.find_for_authentication(warden_conditions)
    subdomain = warden_conditions.delete(:subdomain)
    Thread.current[:current_tenant_id] = Tenant.where(subdomain: subdomain).first.id
    super
  end
  </code></pre>
  
  <hr>
  
  <p><strong>SOLUTION 2:</strong></p>
  
  <p>UPDATE: This too have problems. This does not work always.</p>
  
  <p>Use an instance variable instead.</p>
  
  <p>In user.rb</p>
  
  <pre><code>devise ...,
         request_keys: [:subdomain]
  
  
  default_scope { where(tenant_id: (Tenant.current || @tenant_id)) }
  
  protected
  
  def self.find_for_authentication(warden_conditions)
    subdomain = warden_conditions.delete(:subdomain)
    @tenant_id = Tenant.where(subdomain: subdomain).first.id
    super
  end
  </code></pre>
  
  <hr>
  
  <p>I would like to know which would be a safer approach, or whether there is a better way of solving this.</p>