Skip to content

Commit

Permalink
Add support for multi-subdomain session by setting cookie host in ses…
Browse files Browse the repository at this point in the history
…sion cookie so you can share session between www.example.com, example.com and user.example.com. [#4818 state:resolved]

Signed-off-by: David Heinemeier Hansson <david@loudthinking.com>
  • Loading branch information
guillermo authored and dhh committed Jun 10, 2010
1 parent e11bb95 commit c4d6245
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 1 deletion.
5 changes: 5 additions & 0 deletions actionpack/CHANGELOG
Original file line number Original file line Diff line number Diff line change
@@ -1,5 +1,10 @@
*Rails 3.0.0 [Release Candidate] (unreleased)*

* Add support for multi-subdomain session by setting cookie host in session cookie so you can share session between www.example.com, example.com and user.example.com. #4818 [Guillermo Álvarez]

* Removed textilize, textilize_without_paragraph and markdown helpers. [Santiago Pastorino] * Removed textilize, textilize_without_paragraph and markdown helpers. [Santiago Pastorino]



*Rails 3.0.0 [beta 4] (June 8th, 2010)* *Rails 3.0.0 [beta 4] (June 8th, 2010)*


* Remove middleware laziness [José Valim] * Remove middleware laziness [José Valim]
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -121,7 +121,12 @@ def call(env)
unless options[:expire_after].nil? unless options[:expire_after].nil?
cookie[:expires] = Time.now + options.delete(:expire_after) cookie[:expires] = Time.now + options.delete(:expire_after)
end end


if options[:domain] == :all
top_level_domain = env["HTTP_HOST"].split('.')[-2..-1].join('.')
options[:domain] = ".#{top_level_domain}"
end

request = ActionDispatch::Request.new(env) request = ActionDispatch::Request.new(env)
set_cookie(request, cookie.merge!(options)) set_cookie(request, cookie.merge!(options))
end end
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ module Session
# integrity defaults to 'SHA1' but may be any digest provided by OpenSSL, # integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
# such as 'MD5', 'RIPEMD160', 'SHA256', etc. # such as 'MD5', 'RIPEMD160', 'SHA256', etc.
# #
# * <tt>:domain</tt>: Restrict the session cookie to certain domain level.
# If you use a schema like www.example.com and wants to share session
# with user.example.com set <tt>:domain</tt> to <tt>:all</tt>
#
# :domain => nil # Does not sets cookie domain. (default)
# :domain => :all # Allow the cookie for the top most level
# domain and subdomains.
#
# To generate a secret key for an existing application, run # To generate a secret key for an existing application, run
# "rake secret" and set the key in config/environment.rb. # "rake secret" and set the key in config/environment.rb.
# #
Expand Down
29 changes: 29 additions & 0 deletions actionpack/test/dispatch/session/cookie_store_test.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -185,6 +185,35 @@ def test_session_store_with_expire_after
end end
end end


def test_session_store_with_explicit_domain
with_test_route_set(:domain => "example.es") do
get '/set_session_value'
assert_match /domain=example\.es/, headers['Set-Cookie']
headers['Set-Cookie']
end
end

def test_session_store_without_domain
with_test_route_set do
get '/set_session_value'
assert_no_match /domain\=/, headers['Set-Cookie']
end
end

def test_session_store_with_nil_domain
with_test_route_set(:domain => nil) do
get '/set_session_value'
assert_no_match /domain\=/, headers['Set-Cookie']
end
end

def test_session_store_with_all_domains
with_test_route_set(:domain => :all) do
get '/set_session_value'
assert_match /domain=\.example\.com/, headers['Set-Cookie']
end
end

private private


# Overwrite get to send SessionSecret in env hash # Overwrite get to send SessionSecret in env hash
Expand Down

4 comments on commit c4d6245

@splattael
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this work for .co.uk?

@mitchellh
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to not work for ".co.uk" domains. I didn't actually run the test suite against it but just running the code through my head:

env["HTTP_HOST"] = "foo.co.uk"
top_level_domain = env["HTTP_HOST"].split('.')[-2..-1].join('.') # this becomes "co.uk"
options[:domain] = ".#{top_level_domain}"

So the final result is ".co.uk" which is an invalid cookie domain. I don't think there is any clean way to implement this without a blacklist sort of approach (this is how Firefox & Opera validate cookie domains, for example), so I'd recommend we just stick with the manual :domain => ".mydomain.com" approach, which isn't too difficult.

@guillermo
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said in the lighthouse ticket, this is the simplest aproach, and for 3th level domains, you need to continue using the old approach.

https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4818-share-session-between-subdomains#ticket-4818-3

@mitchellh
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. I'm okay with that since all my domains are in the ".com" or other single TLD domain. I guess it just concerns me that ":domain => :all" is misleading then for "co.uk" and other users.

Please sign in to comment.