Sharing session across rails apps on different subdomains - ruby-on-rails

I am trying to implement a single-sign-on solution for multiple rails (v3.2) apps hosted at different subdomains of example.com
One app serves as an identity provider, uses devise for auth, and sits at users.example.com
The other apps rely on the identity provider for authentication, use devise+omniauth, with domains of [app1.example.com, app2.example.com, and example.com].
This blog entry inspired much of my implementation: http://blog.joshsoftware.com/2010/12/16/multiple-applications-with-devise-omniauth-and-single-sign-on/
I have it working fine, but the problem remains that the sessions are not shared so after I log in on the identity provider, I still have to make a call from each of the other apps to authenticate and I need this to be seemless to the user.
I tried using the same secret token at secret_token.rb, same session key at session_store.rb and :domain => :all (also tried '.example.com' and 'example.com' as values). Still no luck.
Doing the above, I see in a session.inspect that after login on the identity provider the session variable "warden.user.user.key" is populated. When I immediately go to the app on app1.example.com, the session.inspect shows the same session_id and _csrf_token but the "warden.user.user.key" variable is now missing.
I feel like I am missing something silly.. Any ideas what that may be?

I think there is another SO question about getting a single cookie to work across subdomains that would answer yours:
https://stackoverflow.com/a/10403338/2573896
Also, I can imagine that using a memcached cluster with dalli and memcached as your session store would work as well:
http://awesomerails.wordpress.com/2011/08/23/rails-3-memcached-session-store/
For the purpose of your application, the first solution makes more sense though.

Related

Rails, Devise, and multiple domains

Let's say I have an application that's going to be accessed from completely different domains that all point at the same server*:
example.com, example.net, foobar.com, ...
I have a Devise based authentication system that's worked fine before. However, the goal is now to add HTTPS to the sign in system. The problem is, as it turns out, there is no way to host more than one HTTPS website on the same IP address**. To resolve this problem, I set up the login pages to always POST to https://secure.example.com. As far as I can tell, this is working fine. Devise seems to have no qualm with it. However, the tricky part is that the user now needs to be redirected to foobar.com, which also needs to understand that the user is logged in. I pass the site to return to in a hidden parameter in the login form, and the redirection works fine. I still have no way to inform foobar.com that the user is now logged in.
I've managed to set it up so that, upon being returned to foobar.com, it copies the user's session cookie for secure.example.com into a new cookie for foobar.com. This part is working fine. However, in the Rails console, the web requests for secure.example.com and foobar.com - with the same cookie sent for each - produce two completely different sessions and therefore, it's no wonder Devise acts like the user was never logged in to foobar.com
Does anyone know why this wouldn't work - why two identical web requests (only the domain of the request URI was different - I tried it in Firebug, too) would produce two completely different sessions in a Rails 3 app with different, yet consistent, session ids? More to the point, does anyone know how to MAKE this work?
* assume, for the purposes of this exercise, that this is unavoidable and the sites cannot be hosted all under different subdomains, and that the number of domains required is too great to get a separate IP address for each.
** unless they're subdomains and you have an *.example.com cert, but that's beside the point.
If you're already using Devise, I suggest you try using token authenticatable. You can generate a token for the user in question, redirect them with the token to sign in, and then quickly expire the token after they have signed in.
You could also try rolling your own OAuth provider with doorkeeper.

OmniAuth dynamic client options site within the strategy

I have a rails app set up as an OAuth2 provider (using Doorkeeper). The app uses a different subdomain per user account (or an entirely different domain through a cname record)
i.e.
user1.myrailsapp.com
user2.myrailsapp.com
www.mycustomdomain.com
On the provider side, everything is working as expected.
I also have a second app that is a client making use of the first app's exposed API. I have a version of the client working but only with a hard coded site url in the OmniAuth strategy.
The question is, how can I dynamically set the strategy url on a per request basis.
For anyone interested, the solution is in the use of dynamic providers: https://github.com/intridea/omniauth/wiki/Dynamic-Providers
Rails.application.config.middleware.use OmniAuth::Builder do
provider :mystrategy, ENV["OAUTH_ID"], ENV["OAUTH_SECRET"],
:setup => lambda{|env|
env['omniauth.strategy'].options[:client_options].site = env['rack.session']['oauth_site']
}
end
One option is don't do it that way.
I have a similar app and ran into the same issue. However after thinking about it for a moment I realised that I didn't want to send them to a strategy provider URL on the user account's subdomain because the request wasn't yet fully authenticated (it hasn't been processed by the rails app yet).
Also for the first time a user logs in the user account subdomain hadn't yet been set up, so it would have been impossible to route there.
So instead, I have the strategy callback URL set to the main website. After the signin request is processed, session set up and everything, then I redirect the client onto their user subdomain. Takes out a whole load of pain.

How do I let a user sign in from a different domain on Authlogic?

[This is slightly different than a previous question about having multiple domains share the same cookie. It seemed like there wasn't an easy way to do that.]
I have a application at application.com. A customer has app.customer.com pointed at my site on Heroku, and I have everything set up so that it renders a specific version of app correctly. The issue is that I want a user at app.customer.com to be able to login. I believe authlogic is now setting the cookie on application.com, so while it verifies the credentials, no session on customer.com is ever created.
Since cookies cannot be shared across domains, you probably need to save an identifier in a database and also pass it through the url so when the client browser hits the new domain, it sends the token for the new domain session to see and match.
It should be a long cryptographically safe token, like a UUID to keep it from being guessed by attackers.
I'm not sure how the authlogic piece fits in though.

How can I share user sessions across multiple domains using Rails?

Is anyone aware of any gems, tutorials, or solutions enabling a user to sign in to a website at one domain and automatically given access to other partner domains in the same session?
I have two rails apps running, let's call them App-A and App-B. App-A has a database associated with it, powering the registration and login at App-A.com. I'd now like to give all of those users with App-A.com accounts access to App-B.com, without making them reregister or manually login to App-B.com separately.
Thanks in advance for any help!
--Mark
You can set the same session_key in both apps. In appA environment.rb change the session_key, like this
Rails::Initializer.run do |config|
...
config.action_controller.session = {
:session_key => '_portal_session',
:secret => '72bf006c18d459acf51836d2aea01e0afd0388f860fe4b07a9a57dedd25c631749ba9b65083a85af38bd539cc810e81f559e76d6426c5e77b6064f42e14f7415'
}
...
end
Do the same in AppB. (remember to use the very same secret)
Now you have shared sessions. Let's say you use restfull_authentication, wich sets a session variable called user_id. When you authenticate in appA it sets the user_id in the session. Now, in appB you just have to verify if user_id exists in the session.
This is the overall schema, you can elaborate more using this idea.
If you want to create single sign-on solution for your applications then I recommend to take a look at RubyCAS solution. It could be used also to provide single sign-on for other non-Rails applications as well as you can integrate authentication with LDAP or other authentication providers.

Rails - Multiple top level domains and a single session/cookie

I've been struggling with this for quite awhile and haven't been able to
find a solution. I need a user to be able to view multiple top level
domains with a single login.
My understanding is that this needs to be set in environment.rb and
called with before_dispatch. This is what I've come up with:
require 'activesupport'
require 'dispatcher'
module ActionController
class Dispatcher
def set_session_domain
ActionController::Base.session_options.update :session_domain => "#{#request.host}"
end
before_dispatch :set_session_domain
end
end
However, this does not seem to be working when I try and pull the values
from session[:session_domain].
Any help is greatly appreciated.
This one is a bit tricky. Since cookies can only be assigned to (and retrieved from) the current domain ("forms.example.com", say) and parent domains (".example.com", but not ".com"), but NOT to other domains ("othersite.com"), you'll have to find yourself another solution. This has nothing to do with Rails, but with how cookies work.
EDIT: Sessions rely on a client-specific handle, stored in a cookie, which is why sessions also don't work cross-domain.
This site has one possible solution for creating a cross-domain cookie, and it's the cleanest way I know of, although it may have some security implications. A more complicated version would have the servers communicate directly through some secure channel.
If you're looking for a more general-purpose single-login service, try implementing some form of OpenID.
For sub-domains in Rails 2.3
ActionController::Base.session = { :domain => ".mydomain.com" }
For top-level domains try this middleware.
I've been playing with the above middleware at the moment and it does not quite work as expected. If you do use the middleware you do not need the above code as it handles sub-domains as well.
You will probably need something like RubyCAS if you want authentication across domains regardless of whether they're top-level or subdomains.
Your question is not really precise enough IMHO. Do you want a single cookie for all Rails apps you have or is it within the context of a single one? If the former, you want to look at solutions using database-backed sessions or something along the line of RubyCAS to implement the CAS protocol.
Both Keltia and zuk are right, Answer is rubyCAS, We have do that integration and it allows
SSI - Single sign -in
You sign to one site and you are automatically signed to the other
SSO - Single Sign Out
You sign out from one site and automatically you signed out from the other
For us this is a proven solution and not a hard one to implement
we are using it in
http://www.cabslk.com and www.ticketslk.com
cheers,
Sameera

Resources