Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to share login sessions across subdomains in Rails 3?

All the info I've found on the internet about this says to use something like

Login::Application.config.session_store :cookie_store, :key => '_login_session', :domain => '.domain.com'

And use the same key for all the subdomains that I want to share that session. When I do this, the authentication is not being passed between subdomains. In fact, when I visit any of the supposedly shared sessions, the initial session gets overwritten

i.e. on login.domain.com, I run the authentication, which returns the user name and session user_id. I then go to sub.domain.com, which should return the same info as login.domain.com, but does not. Following this, I go back to login.domain.com and I am no longer authenticated there, either.

On sub.domain.com, the session_store.rb file looks like:

Something::Application.config.session_store :cookie_store, :key => '_login_session', :domain => '.domain.com'

I have used :all for the :domain value, as well, with the same outcome. And if I remove the :domain setting on the above, then the initial session does not get overwritten, but it also does not get shared.

When I look at the cookies in Cookie Editor for Firefox, both subdomains are using the same cookie name, but the authentication is not being shared. It's a pretty basic Users table, and I am using OpenID and OAuth to perform authentication with Omniauth

like image 558
aperture Avatar asked Feb 25 '11 22:02

aperture


People also ask

How do I share session state across subdomains?

To share session data (via browser cookies) across across subdomains, lets say Page on example.com and Page on example.com , you can declare the cookie's domain as ". Example Domain " which would allow both subdomains to read the session data.

Can you share cookies across subdomains?

To share cookies across subdomains, you can simply create cookies with the domain directive set to the parent domain, in this case, example.com, rather than either of the specific subdomains.

Does session storage persist across subdomains?

Actually, this is a browser storage security issue, and in fact, none of LocalStorage/WebSQL/IndexedDB can be shared across subdomains. They are part of the “same-origin policy” which sandboxes data.

Can you have 2 subdomains?

You create subdomains to help organize and navigate to different sections of your main website. Within your main domain, you can have as many subdomains as necessary to get to all of the different pages of your website.


2 Answers

update: the suggested solution is not that ugly after all, ad-exchanges and DSPs/SSPs use the same technique to exchange a visitor's session ID so they can better target the visitor with ads (the next time that visitor pops up in their network again)


If you can circumvent the browser cross-domain barrier, you can do it. For example, JSONP is specifically built for this purpose. And yes, session info is always stored centrally, otherwise if you get a request with a session ID of "zigzag", how can you check if it is valid?

"Those" sites that authenticate on login.domain.com might use an ajax proxy, or use other method to get through the cross-domain problem.

The oldest "trick" is to create a hook in your application that looks like an image, as images can be loaded from everywhere.

For example, on login.domain.com you authenticate the user, sent to the server and back with a response, and a cookie will be stored under login.domain.com with the session ID (which is stored in the server as well). Then - from Javascript - you GET an image, with the session ID attached, like http://any.domain.com/path/image.jpg?sessionID=abcd -> any cookies sent back in the response will be stored under any.domain.com

Another solution - which is as ugly as the previous - is to use a hidden iframe to call to any.domain.com (when a successful authentication happens), that request will return a response, and its cookies will be written under the any.domain.com domain.

If you have a multitudes of subdomains, and you can complicate your architecture a bit, I highly advise that you create a proxy, and make it available to every subdomain on the same IP address. Then no matter where the user comes in, the authentication process will always be the same, for every subdomain.

like image 147
karatedog Avatar answered Oct 03 '22 22:10

karatedog


For some reason prefixing the domain with a dot did not work (rails 3.2.11) for me either. 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:

  • Railscasts about Rack
  • Railsguide for Rack
  • Rack documentation for sesssions abstractly and for cookie sessions

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: 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 "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.

like image 41
Evan Avatar answered Oct 03 '22 22:10

Evan