Rails - Redirect to a user's subdomain if it is removed and they are signed in?

858 views Asked by At

I have a multi-tenancy app with different accounts via subdomains. My login system uses cookies. I have a current_user helper in my application controller:

def current_user
  @current_user ||= User.unscoped.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token]
end
helper_method :current_user

I log them in using this code:

cookies[:auth_token] = {value: user.auth_token, domain: :all}

This works well. What I'd like to do is show the user if they are signed into other accounts on the login page of a different account. In other words:

  1. Sign in to account1.derp.com
  2. Visit account2.derp.com/login
  3. Link on the bottom says (You're already signed into account1).

However, I can't seem to access cookies from the other account (this seems to be the intended behavior of the web/browser). So on that page, if I try to access cookies[:auth_token], its nil, because the cookie was for another account/url.

In addition I'd like to redirect the user to the same URL with their subdomain if they are signed in and they remove their subdomain from the URL...which requires the same thing - for me to be able to access the cookies for their account if the subdomain isn't there or its different.

Slack, for example, does both of these things. Try removing your subdomain from a page when logged in - you are automatically redirected back to the same page, with your subdomain (it basically just puts it back for you). Or if you go to the login page for another account, it tells you, "You're already signed in on another account."

I am trying to accomplish this, but as soon as I remove the subdomain, or go to a different one, the cookies[:auth_token] that I set when I logged in is nil. I am looking for some way to keep track of where the user is signed in, so in the case of a subdomain removal or visiting another subdomain, I know that they are already logged in to an account.

Does anyone have any experience with this type of cookie management or any insight on how I could keep track if the user is logged into a different account?

1

There are 1 answers

0
Greg Blass On BEST ANSWER

Ryan Bigg, author of "Multitenancy with Rails," responded to this via twitter.

@greg_blass You need to set the domain option for the session_store to be the root domain. If you don’t do this, sessions will be scoped to where they’re set.

That worked...I can read all the users cookies across domains that way:

cookies[:auth_token] = {value: user.auth_token, domain: "lvh.me"} #set lvh.me to the proper environment for production/test

I wasn't thinking this way because that would allow the user to login to any account (the way my code was working).

So I had a logged_in? helper, which was:

def logged_in?
  !current_user.nil?
end
helper_method :logged_in?

I ended up making another helper:

def logged_in_to_current_account?
  logged_in? and request.subdomain == current_user.account.subdomain
end
helper_method :logged_in_to_current_account?

Then my require_login before_action:

def require_login
  if !logged_in_to_current_account?
    redirect_to login_url, flash: {danger: "You need to log in to see this page."}
  end
end

Now logged_in? really just means that they are logged in somewhere, perhaps a different account, which I can use in the case I described above...and I can use the logged_in_to_current_account? helper for the before_action checks.

Then to finally answer my question, once the cookies can be shared across subdomains (and a lack of a subdomain), this code will redirect to the same page and add the user's subdomain back if it is removed:

if logged_in? and request.subdomain.blank?
  redirect_to controller: controller_name, action: action_name, subdomain: current_user.account.subdomain
end

I hooked that into a before_action and I'm good to go!