Share session (cookies) between subdomains in Rails?

Ruby on-RailsSessionDevise

Ruby on-Rails Problem Overview


I have an app setup where each user belongs to a company, and that company has a subdomain (I am using basecamp style subdomains). The problem that I am facing is that rails is creating multiple cookies (one for lvh.me and another for subdomain.lvh.me) which is causing quite a few breaks in my application(such as flash messages being persistent though out all requests once signed in).

I have this in my /cofig/initilizers/session_store.rb file:

AppName::Application.config.session_store :cookie_store, key: '_application_devise_session', domain: :all

The domain: :all seems to be the standard answer I found on Google, but that doesn't seem to be working for me. Any help is appreciated!

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

As it turns outs 'domain: all' creates a cookie for all the different subdomains that are visited during that session (and it ensures that they are passed around between request). If no domain argument is passed, it means that a new cookie is created for every different domain that is visited in the same session and the old one gets discarded. What I needed was a single cookie that is persistent throughout the session, even when the domain changes. Hence, passing domain: "lvh.me" solved the problem in development. This creates a single cookie that stays there between different subdomains.

For anyone needing further explanation, this is a great link: http://excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/

Solution 2 - Ruby on-Rails

http://excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/

> "The part you want to watch out for here is that if you set :domain => > :all like is recommend in some places, it simply won’t work unless > you’re using localhost. :all defaults to a TLD length of 1, which > means if you’re testing with Pow (myapp.dev) it won’t work either > because that is a TLD of length 2."

In other words you need:

 App.config.session_store ... , :domain => :all, :tld_length => 2

Also a good idea to clear your cookies

Solution 3 - Ruby on-Rails

I was looking for a way to solve this problem without having to explicitly state the domain name, so I could hop between localhost, lvh.me, and whichever domains I would use in production without having to keep editing the session_store.rb file. However, setting "domain: :all" didn't seem to be working for me.

Ultimately I found that I needed to state the tld_length (top level domain length) in that expression. The default tld_length is 1 while example.lvh.me has a tld_length of 2 and 127.0.0.1.xip.io has a tld_length of 5, for example. So what I had in the session_store.rb file for subdomains on lvh.me in development and whatever else in production was the below.

MyApp::Application.config.session_store :cookie_store, key: '_MyApp_session', domain: :all, tld_length: 2

Hope this helps someone, as it took me a long time to find this answer!

Solution 4 - Ruby on-Rails

For some reason replacing :all with the domain did not work (rails 3.2.11) for me. It took a piece of custom Middleware to fix it. A summary of that solution is below.

tl;dr: You need to write a custom Rack Middleware. You need add it into your conifg/environments/[production|development].rb. This is on Rails 3.2.11

Cookie sessions are usually stored only for your top level domain.

If you look in Chrome -> Settings -> Show advanced settings… -> Privacy/Content settings… -> All cookies and site data… -> Search {yourdomain.com} You can see that there will be separate entries for sub1.yourdomain.com and othersub.yourdomain.com and yourdomain.com

The challenge is to use the same session store file across all subdomains.

##Step 1: Add Custom Middleware Class##

This is where Rack Middleware comes in. Some relevant rack & rails resources:

Here is a custom class that you should add in the lib This was written by @Nader and you all should thank him

# Custom Domain Cookie
#
# Set the cookie domain to the custom domain if it's present
class CustomDomainCookie
  def initialize(app, default_domain)
    @app = app
    @default_domain = default_domain
  end

  def call(env)
    host = env["HTTP_HOST"].split(':').first
    env["rack.session.options"][:domain] = custom_domain?(host) ? ".#{host}" : "#{@default_domain}"
    @app.call(env)
  end

  def custom_domain?(host)
    host !~ /#{@default_domain.sub(/^\./, '')}/i
  end
end

Basically what this does is that it will map all of your cookie session data back onto the exact same cookie file that is equal to your root domain.

##Step 2: Add To Rails Config##

Now that you have a custom class in lib, make sure are autoloading it. If that meant nothing to you, look here: https://stackoverflow.com/questions/4073856/rails-3-autoload

The first thing is to make sure that you are system-wide using a cookie store. In config/application.rb we tell Rails to use a cookie store.

# We use a cookie_store for session data
config.session_store :cookie_store,
                     :key => '_yourappsession',
                     :domain => :all

The reason this is here is mentioned here is because of the :domain => :all line. There are other people that have suggested to specify :domain => ".yourdomain.com" instead of :domain => :all. For some reason this did not work for me and I needed the custom Middleware class as described above.

Then in your config/environments/production.rb add:

config.middleware.use "CustomDomainCookie", ".yourdomain.com"

Note that the preceding dot is necessary. See "https://stackoverflow.com/questions/3865345/sub-domain-cookies-sent-in-a-parent-domain-request" for why.

Then in your config/environments/development.rb add:

config.middleware.use "CustomDomainCookie", ".lvh.me"

The lvh.me trick maps onto localhost. It's awesome. See this Railscast about subdomains and this note for more info.

Hopefully that should do it. I honestly am not entirely sure why the process is this convoluted, as I feel cross subdomain sites are common. If anyone has any further insights into the reasons behind each of these steps, please enlighten us in the comments.

Solution 5 - Ruby on-Rails

I came across this while looking for the simplest way to set the cookie to be the root domain. It seems there is some misinformation about the :all option when passed as the domain option. For most domains, it will actually work as expected, setting the cookie to the root domain (e.g. .example.com for test.example.com). I think most people experienced issues since they're using the domain lvh.me to test. The regex used by rails to find a top level domain is defined to be DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/. If you note the last part, you can see that rails interprets lvh.me as a TLD similar to com.au. If your use case needs lvh.me to work, then the :all option won't work properly, however, it appears to be the simplest and best option for most domains.

TL;DR, the correct answer here, assuming you aren't developing on a 3 letter domain (or any domain that confuses the above regex) is to use :all.

Solution 6 - Ruby on-Rails

Rails 4.x (also should be fine with Rails 5/6 versions)

How to get lvh.me:3000 and subdomain in localhost (Rails)

Development: I have shared cookies to adding .lvh.me into session_store.rb,

It will be shared between subdomains on localhost admin.lvh.me:3000, lvh.me:3000 and so on...

#config/initializers/session_store.rb

domain = Rails.env.production? ? ".domain_name.com" : ".lvh.me"

Rails.application.config.session_store :cookie_store, 
                      key: '_app_name_session', domain: domain

Solution 7 - Ruby on-Rails

Did you try

AppName::Application.config.session_store :cookie_store, key: '_application_devise_session', domain: 'lvh.me'  

)

basically we are saying have single cookie for base domain and just ignore sub domain..though this approach has some flaws still ...

Solution 8 - Ruby on-Rails

support rails5

if you want it works with any domain:

Rails.application.config.session_store :cookie_store, key: '_my_app_session', domain: :all, tld_length: 2

To configure per environment you could use the following:

Rails.application.config.session_store :cookie_store, key: '_my_app_session', domain: {
  production: '.example.com',
  development: '.example.dev'
}.fetch(Rails.env.to_sym, :all)

Ref: https://github.com/plataformatec/devise/wiki/How-To:-Use-subdomains

Solution 9 - Ruby on-Rails

If you are using Redis for session store.

if Rails.env.development?
	Rails.application.config.session_store :redis_store, {
	   servers: [
	    { host: 'localhost', port: 6379},
	  ],
	  key: '_app_session',
	  expire_after: 1.day,
	  domain: :all
	}

else
	Rails.application.config.session_store :redis_store, {
	   servers: [
	    { host: HOST_URL, port: PORT},
	  ],
	  key: '_app_session',
	  expire_after: 1.day,
	  domain: '.domain.com',
	  tld_length: 2
	}
	
end	

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWahaj AliView Question on Stackoverflow
Solution 1 - Ruby on-RailsWahaj AliView Answer on Stackoverflow
Solution 2 - Ruby on-RailsmontrealmikeView Answer on Stackoverflow
Solution 3 - Ruby on-RailsFangedParakeetView Answer on Stackoverflow
Solution 4 - Ruby on-RailsEvanView Answer on Stackoverflow
Solution 5 - Ruby on-RailscassanegoView Answer on Stackoverflow
Solution 6 - Ruby on-Rails7urkm3nView Answer on Stackoverflow
Solution 7 - Ruby on-RailsNaveedView Answer on Stackoverflow
Solution 8 - Ruby on-Railscgg5207View Answer on Stackoverflow
Solution 9 - Ruby on-RailsMarcelo AustriaView Answer on Stackoverflow