Delete Session Cookies Across Multiple Subdomains in Rails 3 - ruby-on-rails

I'm building a rails app that works similar to Wufoo. When you sign up you get a subdomain, and you can log in on the home page. The app is working, so that when you log in, you get redirected to your subdomain. The problem is that I can't delete the session on both domains. If you log out at (username.myapp.com), it stays logged in at (myapp.com) and vice versa.
Right now I'm using session[:user_id] = nil to delete the session. Is there a way to delete all the sessions across all domains.
In addition, I appended :domain => :all to my session_store.rb file so I could stay logged in across multiple subdomains.

The key is really how you set your session cookies, because you can't delete a subdomain cookie (username.myapp.com) from a top-level domain (myapp.com). To solve this you'll want all your shared session cookies to be set under the myapp.com domain. To do this, setup your sessions in the following way:
Rails.application.config.session_store :cookie_store, :domain => 'myapp.com'
That way, when you destroy your session (session[:id] = nil) you'll be removing the shared cookie. I believe you will also have to delete the session using session[:id] instead of session[:user_id].

Related

How to store the flash in a different cookie to the Rails session?

We have the Rails session cookie set to be only visible on the subdomain it is issued (i.e. setting domain: nil in the session store configuration). This means there are separate user sessions per subdomain, which is what we want.
However, we would still like the flash messages to be visible across subdomains. So if they log out on foo.oursite.com and are redirected to www.oursite.com, they should see the "logged out successfully" message from the flash, despite it being different subdomains.
So my question is: can we configure the Rails flash system to use a separate cookie to the session store, so we can set domain: :all for that cookie and have the flash be visible across subdomains?
(We are on Rails 5.0)
You could do something like e
Yourapp::Application.config.session_store :cookie_store, key: '_yourapp_session', :domain => :all
But this would mean all your cookies are accessible across sub-domains and would share the logged in session cookie.
You would maybe want to look into storing alerts in the database and retrieve them as needed.

Are browser sessions unique for each subdomain?

Say I'm running a multi-tenant application that gives each organization its own portal via a subdomain.
Example -
orgA.application.com
orgB.application.com
etc...
Each subdomain reads from a different schema/tenant in my PSQL db, but is otherwise the same application.
In my ApplicationController I set the current_user as -
def current_user
if session[:user_id]
#current_user ||= User.find_by_id(session[:user_id])
end
end
There are few admin/superusers such as myself that have a user account on each subdomain. If I log into orgA with my user (id = 22), then my session gets set as user_id: 22.
Now say I want to switch over to orgB, where my user id is 44. If I log into orgB after having set my session in orgA, is there any chance I could accidentally log myself in as the user who is 22 on orgB?
More fundamentally, I'm trying to understand how a browser cookie session is set. From my understanding, it's a hash of variables that are encrypted and cached in the client's browser. Is that set per subdomain? Or do all subdomains of a particular site share the same cache/session cookie?
More importantly, how do I prevent cross pollination of sessions like in the example above? Is my current_user method too basic?
Thanks!
You're fundamentally asking about cookies here, to which the answer is relatively simple: cookies are not shared across subdomains unless you explicitly request it.
When you send the Set-Cookie HTTP header to create a cookie in the user's browser, you can choose whether or not to include a domain configuration option. That option controls which domain the cookie saves under and will be served to.
By default, if you send Set-Cookie with no domain option, the cookie will be set for the current hostname, which includes subdomains. That is, a cookie set on siteA.example.com will not be accessible to siteB.example.com.
If you send a domain option of example.com when you create your cookie on siteA.example.com, then the cookie will be accessible on both example.com and *.example.com, so all your sites will be able to access it.
For your situation, then, you should send the Set-Cookie header with no domain option. That's the default in most setups, including Rails so it's unlikely you need to do anything.

Rails ActiveRecord Session Store in HTML5 SessionStorage Instead of Cookie

My application has a requirement for users to log into different accounts in separate tabs in their browser (we target Chrome specifically). Because Rails uses cookies to store session info, when the user is logged in, they are logged in on all tabs in the browser. I'm using the ActiveRecord session store method, but even the ID for the session is saved as a cookie.
It seems there's a solution in using HTML5's sessionStorage mechanism, which is limited in scope to the tab or window that the user is logged into. It seems all I have to do is direct Rails to save the session info into the sessionStorage rather than cookies. But I can find no information on this at all.
Assuming there's no way to configure the session store to do this in Rails, is it possible to override the ActiveRecord session saving mechanism? Any pointers on where to look for info about how to go about this?
Unlike cookies, sessionStorage entries cannot be created with response headers, and are not automatically included in request headers. This puts a lot of the workload for managing sessionStorage/localStorage-based authentication on client-side Javascript. All authenticated access would have to be through Javascript XHR requests which explicitly include the authentication token.
If you want the user to be able to have several concurrent sessions, and you don't want to build your site as a SPA, then you will have to take an alternate approach with cookies.
One way would be to use multiple domains to force the cookies into separate subspaces. Set a wildcard DNS record and configure your web server to accept all matching requests regardless of prefix. For example, users might by default be at www.yoursite.com. You would provide a 'create new session' link which opens a new tab to a random subdomain, e.g. 1234abcd.www.yoursite.com. This may create a problem if you are using SSL, however; wildcard SSL certificates tend to be much more expensive.
A simpler way would be to educate users about their browsers' private/icognito modes, which maintain independent cookie stores. However, getting users to read documentation is always a challenge.
You now configure the Cookie-based session store through an initializer, probably in config/initializers/session_store.rb. In Rails 3 the session store is a piece of middleware, and the configuration options are passed in with a single call to config.session_store:
Your::Application.config.session_store :cookie_store, :key => '_session'
You can put any extra options you want in the hash with :key, e.g.
Your::Application.config.session_store :cookie_store, {
:key => '_session_id',
:path => '/',
:domain => nil,
:expire_after => nil,
:secure => false,
:httponly => true,
:cookie_only => true
}

active_record_store is set but cookie still created

I have this in session_store.rb
Trunk::Application.config.session_store :active_record_store, :key => '_eg2_session_id', :domain => domain
And I ran the session migration. The session table is there and records are getting created in it, however cookies are also getting created. Any idea why?
The cookie is still required to map the user visiting the site to the session in the database. What you're changing when you change the session_store is where the data is stored.
So, the way you have things set up, this is roughly what happens:
User visits the site
Session created in database
Cookie created with session ID
You store user_id => 5 in the session, and that's added to the database
When you use cookie store, here's what happens:
User visits the site
Cookie created with session data
You store user_id => 5 in the session, and that's added to the cookie
In both cases, a cookie is created to associate the visitor with the session, it's just that the data in the session is stored in a different place.

Configuring Rails App to handle multiple subdomains and multiple cookies

I have a rails app which supports multiple domains and each domain may have multiple subdomains.
Users visiting mydomain1.com do not receive the same experience as mydomain2.com (although the base behaviour of the apps is the same)
Therefore, if a user is logged in to mydomain1.com, it shouldn't then be logged in to mydomain2.com
If a user is logged in to france.mydomain1.com, it should then be logged in to germany.mydomain1.com
Previously, I've handled this by setting the domain in the session store configs:
MyApp::Application.config.session_store :cookie_store, :key => '_MyApp_session', :domain => APP_CONFIG[:domain]
I'm trying to work out the best way to handle this with multiple domains?
I've tried hacking around ActionDispatch::Callback but the request is not available from within there.
Can anybody suggest a good way of supporting multiple cookies from within one app?
Ideally I'd like to create a fresh cookie for each subdomain.
You should do that:
class ActionDispatch::Session::MultiDomainStore < ActionDispatch::Session::CookieStore
def initialize(app, options = {})
super(app, options.merge!(:domain => compute_domain(app)))
end
def compute_domain(app)
...
end
end
MyApp::Application.config.session_store :multi_domain_store, :key => '_MyApp_session'
I.e. your domain should start with the dot.
It shouldn't be an issue as cookies are only valid per domain. You can have a _MyApp_session for example1.com and one for example2.com. The cookies are managed by the browser and only sent to the host if the domain matches.
Say you visit example1.com and log in and you will get a cookie with the value abcdef123. Then you log into example2.com and you will get another cookie with a random string uvwxyz890.
If you return to example1.com later, the browser will only send the cookies that are valid for this domain to your app. Your app won't have to manage anything and you don't have to hack anything.

Resources