I have a Rails app that sets a cookie and does a redirect to another server once the user is logged in. However, the cookie that the Rails app sets isn't seen by the server for some reason. I've tried setting http_only to false but I still can't even see the cookie unless the domain is the same as my Rails app. Here's the code I'm using to set the cookie:
cookies[:dev_appserver_login] =
{ :value => "#{email}:#{nick}:#{admin}:#{hsh}",
:domain => "webserver-to-redirect-to",
:expires => 30.days.from_now }
redirect_to session[:dest_url]
If I manually create a cookie with the Web Developer extension in Firefox it works fine, but not when Rails does it. Any ideas?
What are the redirecting and redirected-to servers? You can only set ‘domain’ to the current hostname or a parent domain, so if you're on a.example.com and you're redirecting to b.example.com, you have to set ‘domain’ to .example.com, not b.example.com as implied in the code snippet.
(And open domains like the .com TLD aren't themselves allowed as domain values, so if you want to pass a cookie from a.example.com to b.somewhereelse.com you will need a more complicated solution probably involving changing the code on somewhereelse.com.)
I still can't even see the cookie unless the domain is the same as my Rails app.
That's how cookies are supposed to work. If you're accessing it directly by IP, then as far as the web browser is concerned, your 'domain' is just your IP, so the same rules apply.
You can get around this in development mode by editing your /etc/hosts file and creating host names for your apps
127.0.0.1 app1.localdev.com, app2.localdev.com
Then, when the cookie is created set the domain to '.localdev.com' (note the preceeding period') which will allow any app at any subdomain of localdev.com to read it.
Another broader solution (which is better for production deploys, but more work to set up) is to set up a path proxy for the sub-app so requests to appdomain.com go to app1 and requests to appdomain.com/other-app/ are proxied to the other app. This lets them share the root domain and easily share cookies.
Related
I'm trying to figure out how ASP.NET internally validates that a cookie will allow the user to access the application.
CookieAuthenticationMiddleware will set .AspNet.Cookies with an encrypted value. After .NET successfully decrypts the cookie on a request, what validation occurs then?
Developing locally with IISExpress if I have an application (#1) that sets an authentication Cookie after the user logs in, and I create a complete new application (#2) also running on localhost, that is also using CookieAuthentication. When I access #2 it will read the cookie from #1 and allows the user to access the application as well.
I'm trying to understand what the limits are for cookie authentication.
There's not really any "validation" per se. The cookie's encrypted key is used to reference the user that should be "logged in". It works in a very similar way to sessions, where the session cookie holds an encrypted session id that the server can use to look up and restore the session via.
The encryption/decryption is based on the machine key, which either may be explicitly set in the Web.config or generated automatically by ASP.NET. Only applications that share the same machine key may decrypt the cookie, which is why it's so important to protect your machine key.
Anyways, there's two factors involved here. First, cookies are domain bound: only the domain or subdomains of the domain the cookie is set on will be given the cookie. This is handled by the client (i.e. browser). Your two applications currently are able to both see the cookie because they're both running on localhost. However, if you were to deploy one at foo.com and the other at bar.com, then they would no longer be able to see each other's cookies.
Second, the machine key is typically by server (unless you explicitly set it in the Web.config per app). As a result, sites running on the same machine can usually decrypt each other's cookies (assuming they see them in the first place, which again, is based on their domain).
It's not clear whether you're happy or not about this arrangement. If your goal is to segregate the two sites running locally, such that they don't share cookies, you have a couple of options.
You can explicitly set a different machine key for each site in their respective Web.config files. They'll still receive any cookies set by the other site, but they'll no longer be able to decrypt them, which basically results in them being ignored.
You can customize the auth cookie name. Instead of using the default cookie name you can make one .Site1.Auth and the other .Site2.Auth. Then, even though either site will also receive the cookie for the other site, it will simply ignore it, because it's not the auth cookie for it.
If, however, you're intending to rely on this behavior in production as well (i.e. you actually want logging into one site to log you into the other as well), then you'll need to explicitly set the machine key to the same value in both site's Web.config files. Additionally, you'll need to deploy them on the same domain, or at least subdomains of that domain. In the case of subdomains, you'll need to set the cookie domain to be the wildcard domain .mydomain.com for both. Then, you could have one at foo.mydomain.com and another at bar.mydomain.com, and they'd both see the cookie because it was set on .mydomain.com. If you leave it the default, set on the actual domain of the site, then bar.mydomain.com could not see a cookie set by foo.mydomain.com because that cookie would be explicitly set only for foo.mydomain.com.
The primary validations are encryption and expiration. If apps share an encryption context (e.g. machine key) then they can share auth cookies (providing other client side sharing rules like domains and paths are satisfied). So yes it's expected that two apps using IIS Express localhost on the same machine would share cookies by default.
The expiration is also embedded in the encrypted value so the client can't tamper with it.
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.
I'm running into an issue with getting the right cookies for the current subdomain. I have two different instances of the same application, one running at the www subdomain and the other running at the test subdomain, but it seems like the test app is pulling in cookies from the www instead, even if there are explicit cookies for .test.domain.com (because it's different instances the same application, the cookie name is the same, but I am not looking to share sessions or cookies between the two instances).
Is there any way to specify what domain Rails should be looking for its cookies for? I have config.cookies.domain and config.action_controller.session[:domain] both set to "test.domain.com" (on the test app), but it doesn't seem to make any difference (and, as well, the cookies being set by the test domain are for some reason ending up with the domain .test.domain.com instead of test.domain.com and I'm not sure why, or if it's relevant).
Update: To make this more complex, I realized I do actually need the dot-prefaced cookie domains (ie. .test.domain.com and .domain.com), because in general, subdomains are supposed to share the main domain's cookies and session (it's only that the test subdomain is actually it's own application rather than being a subdomain of the main application). Obviously .domain.com cookies will apply to test.domain.com, but is there any way to make the explicitly test.domain.com ones take priority?
I have read
How to give cname forward support to saas software
Rails - Multiple top level domains and a single session/cookie
But I am unable to get a solution for the following setup:
A SaaS Webapp in Rails is running under example.com
All users have a sumbdomain e.g. user1.example.com
Users can create a cname forwarding eg. exampleapp.user1.com -> user1.example.com
It is all working until a user tries to log in via exampleapp.user1.com. The SaaS app fails to set the session domain right, because it is configured static on app startup.
config.action_controller.session = {
:session_key => '_example_session',
:domain => ".example.com",
:secret => 'abc'
}
The Request fails with a ActionController::InvalidAuthenticityToken. And that is correct, because the domain changed from .example.com to exampleapp.user1.com.
How do I change the domain config during runtime? I know that the "incoming" domain exampleapp.user1.com belongs to user1, so I want to do something like that in the controller:
session :domain => 'exampleapp.user1.com'
Or can I always set the session domain on the current request domain?
I know that it's possible somehow, because some apps provide that functionality.
Thanks in advance!
:domain => :all on the cookie config may work.
For CNAME'd domains, it will be set to .theirdomain.com
For your custom subdomain, it will go to .yourdomain.com, which may or may not be good
Just don't set the domain, since apparently you don't need to share a session cookie across example.com and user1.example.com. By not specifying a domain, the default cookie behavior is just to be set for the current request domain.
In my rails app, when I log in at the www.site.com address, I am logged in just fine. Although without logging out, I go to the site, http://site.com I am logged out, but still logged in at the www.site.com address if I go back.
I can't find anything to set in my environment variables, any idea as to how to keep this session across all domains on my domain?
Set the session cookie properly; that is, for .site.com and site.com rather than just for www.site.com.
When you set a session cookie for "site.com", that will be different than "www.site.com." You need to specify the "cookie_domain" as ".site.com" which will set the cookie or all subdomains as well. In PHP, you could use ini_set or session_set_cookie_params to set session.cookie_domain. In Rails, you can either add a small script to the enviroment.rb - something like:
ActionController::Base.session_options[:session_domain] = '.site.com'
(in this case you might also do some switching based on the domain name in production/test/development env's) or try some other configuration options.
Here's more than you'd ever want to know on the subject.
You should redirect www.site.com to site.com (or the other way around). If you don't do that, google may think it's two different sites.
In rails 2.3 this has been changed to:
config.action_controller.session[:domain] = '.example.com'
or if the session variable hasn't been created yet
config.action_controller.session = {:domain => '.example.com'}
See Losing session in rails 2.3.2 app using subdomain
since they alias www. to .; couldn't you just prepend www. onto the .?