diff options
author | Alex Speller <alex@alexspeller.com> | 2020-02-01 03:16:31 +0000 |
---|---|---|
committer | Jeremy Evans <code@jeremyevans.net> | 2020-02-04 07:43:16 -0800 |
commit | dbd33f7e8c2149332636e32301c295197f046490 (patch) | |
tree | 119f3560ba2afb0ebd4731cabf06befb81e736ef | |
parent | 59985351196ad348dbe53b799333ad24d8d64404 (diff) | |
download | rack-dbd33f7e8c2149332636e32301c295197f046490.tar.gz |
Allow passing through same_site option to session cookie when using Rack::Session::Cookie middleware
Recently, rack added support for SameSite=None cookies: https://github.com/rack/rack/pull/1358
However there is currently no way to set these cookies using the Rack::Session::Cookie
middleware without monkeypatching.
This pull request allows setting the SameSite value either by direct, literal
passthrough to the add_cookie_to_header method, or by passing a callable.
The callable option is required because some browsers are incompatible with
some values of the header, so it needs to be [different per-browser](https://www.chromium.org/updates/same-site/incompatible-clients).
Static usage:
```ruby
use Rack::Session::Cookie, secret: 'supersecret', same_site: :none
```
Dynamic usage:
```ruby
use Rack::Session::Cookie,
secret: 'supersecret',
same_site: lambda { |req, res| SameSite.value(req.user_agent) }
```
-rw-r--r-- | lib/rack/session/abstract/id.rb | 6 | ||||
-rw-r--r-- | lib/rack/session/cookie.rb | 1 | ||||
-rw-r--r-- | test/spec_session_cookie.rb | 17 |
3 files changed, 24 insertions, 0 deletions
diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index 74fd98f9..cb011359 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -393,6 +393,12 @@ module Rack cookie[:value] = cookie_value(data) cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after] cookie[:expires] = Time.now + options[:max_age] if options[:max_age] + + if @same_site.respond_to? :call + cookie[:same_site] = @same_site.call(req, res) + else + cookie[:same_site] = @same_site + end set_cookie(req, res, cookie.merge!(options)) end end diff --git a/lib/rack/session/cookie.rb b/lib/rack/session/cookie.rb index bb541396..3b82b41d 100644 --- a/lib/rack/session/cookie.rb +++ b/lib/rack/session/cookie.rb @@ -118,6 +118,7 @@ module Rack Called from: #{caller[0]}. MSG @coder = options[:coder] ||= Base64::Marshal.new + @same_site = options.delete :same_site super(app, options.merge!(cookie_only: true)) end diff --git a/test/spec_session_cookie.rb b/test/spec_session_cookie.rb index 0a240b9f..ce85ba32 100644 --- a/test/spec_session_cookie.rb +++ b/test/spec_session_cookie.rb @@ -196,6 +196,23 @@ describe Rack::Session::Cookie do response.body.must_equal '{"counter"=>1}' end + it "passes through same_site option to session cookie" do + response = response_for(app: [incrementor, same_site: :none]) + response["Set-Cookie"].must_include "SameSite=None" + end + + it "allows using a lambda to specify same_site option, because some browsers require different settings" do + # Details of why this might need to be set dynamically: + # https://www.chromium.org/updates/same-site/incompatible-clients + # https://gist.github.com/bnorton/7dee72023787f367c48b3f5c2d71540f + + response = response_for(app: [incrementor, same_site: lambda { |req, res| :none }]) + response["Set-Cookie"].must_include "SameSite=None" + + response = response_for(app: [incrementor, same_site: lambda { |req, res| :lax }]) + response["Set-Cookie"].must_include "SameSite=Lax" + end + it "loads from a cookie" do response = response_for(app: incrementor) |