Merge pull request #13945 from rails/json_cookie_serializer_improvements

Cookies serializer improvements
This commit is contained in:
Guillermo Iguaran 2014-02-13 09:41:13 -05:00
commit de5ef15398
16 changed files with 318 additions and 125 deletions

@ -1,3 +1,21 @@
* Add new config option `config.action_dispatch.cookies_serializer` for
specifying a serializer for the signed and encrypted cookie jars.
The possible values are:
* `:json` - serialize cookie values with `JSON`
* `:marshal` - serialize cookie values with `Marshal`
* `:hybrid` - transparently migrate existing `Marshal` cookie values to `JSON`
For new apps `:json` option is added by default and `:marshal` is used
when no option is specified to maintain backwards compatibility.
*Łukasz Sarnacki*, *Matt Aimonetti*, *Guillermo Iguaran*, *Godfrey Chan*, *Rafael Mendonça França*
* `FlashHash` now behaves like a `HashWithIndifferentAccess`.
*Guillermo Iguaran*
* Set the `:shallow_path` scope option as each scope is generated rather than
waiting until the `shallow` option is set. Also make the behavior of the
`:shallow` resource option consistent with the behavior of the `shallow` method.
@ -16,21 +34,6 @@
*Josh Jordan*
* Add `:serializer` option for `config.session_store :cookie_store`. This
changes default serializer when using `:cookie_store`.
It is possible to pass:
* `:json` which is a secure wrapper on JSON using `JSON.parse` and
`JSON.generate` methods with quirks mode;
* `:marshal` which is a wrapper on Marshal;
* serializer class with `load` and `dump` methods defined.
For new apps `:json` option is added by default and :marshal is used
when no option is specified.
*Łukasz Sarnacki*, *Matt Aimonetti*
* Ensure that `request.filtered_parameters` is reset between calls to `process`
in `ActionController::TestCase`.

@ -84,8 +84,6 @@ module Session
autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
autoload :CacheStore, 'action_dispatch/middleware/session/cache_store'
autoload :JsonSerializer, 'action_dispatch/middleware/session/json_serializer'
autoload :MarshalSerializer, 'action_dispatch/middleware/session/marshal_serializer'
end
mattr_accessor :test_app

@ -89,7 +89,7 @@ class Cookies
ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze
SECRET_TOKEN = "action_dispatch.secret_token".freeze
SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze
SESSION_SERIALIZER = "action_dispatch.session_serializer".freeze
COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze
# Cookies can typically store 4096 bytes.
MAX_COOKIE_SIZE = 4096
@ -181,7 +181,7 @@ def initialize(*args)
def verify_and_upgrade_legacy_signed_message(name, signed_message)
@legacy_verifier.verify(signed_message).tap do |value|
self[name] = value
self[name] = { value: value }
end
rescue ActiveSupport::MessageVerifier::InvalidSignature
nil
@ -212,7 +212,7 @@ def self.options_for_env(env) #:nodoc:
secret_token: env[SECRET_TOKEN],
secret_key_base: env[SECRET_KEY_BASE],
upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?,
session_serializer: env[SESSION_SERIALIZER]
serializer: env[COOKIES_SERIALIZER]
}
end
@ -374,28 +374,89 @@ def []=(name, options)
end
end
class JsonSerializer
def self.load(value)
JSON.parse(value, quirks_mode: true)
end
def self.dump(value)
JSON.generate(value, quirks_mode: true)
end
end
# Passing the NullSerializer downstream to the Message{Encryptor,Verifier}
# allows us to handle the (de)serialization step within the cookie jar,
# which gives us the opportunity to detect and migrate legacy cookies.
class NullSerializer
def self.load(value)
value
end
def self.dump(value)
value
end
end
module SerializedCookieJars
MARSHAL_SIGNATURE = "\x04\x08".freeze
protected
def needs_migration?(value)
@options[:serializer] == :hybrid && value.start_with?(MARSHAL_SIGNATURE)
end
def serialize(name, value)
serializer.dump(value)
end
def deserialize(name, value)
if value
if needs_migration?(value)
Marshal.load(value).tap do |v|
self[name] = { value: v }
end
else
serializer.load(value)
end
end
end
def serializer
serializer = @options[:serializer] || :marshal
case serializer
when :marshal
Marshal
when :json, :hybrid
JsonSerializer
else
serializer
end
end
end
class SignedCookieJar #:nodoc:
include ChainedCookieJars
include SerializedCookieJars
def initialize(parent_jar, key_generator, options = {})
@parent_jar = parent_jar
@options = options
secret = key_generator.generate_key(@options[:signed_cookie_salt])
@verifier = ActiveSupport::MessageVerifier.new(secret)
@verifier = ActiveSupport::MessageVerifier.new(secret, serializer: NullSerializer)
end
def [](name)
if signed_message = @parent_jar[name]
verify(signed_message)
deserialize name, verify(signed_message)
end
end
def []=(name, options)
if options.is_a?(Hash)
options.symbolize_keys!
options[:value] = @verifier.generate(options[:value])
options[:value] = @verifier.generate(serialize(name, options[:value]))
else
options = { :value => @verifier.generate(options) }
options = { :value => @verifier.generate(serialize(name, options)) }
end
raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
@ -419,13 +480,14 @@ class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc:
def [](name)
if signed_message = @parent_jar[name]
verify(signed_message) || verify_and_upgrade_legacy_signed_message(name, signed_message)
deserialize(name, verify(signed_message)) || verify_and_upgrade_legacy_signed_message(name, signed_message)
end
end
end
class EncryptedCookieJar #:nodoc:
include ChainedCookieJars
include SerializedCookieJars
def initialize(parent_jar, key_generator, options = {})
if ActiveSupport::LegacyKeyGenerator === key_generator
@ -437,12 +499,12 @@ def initialize(parent_jar, key_generator, options = {})
@options = options
secret = key_generator.generate_key(@options[:encrypted_cookie_salt])
sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: serializer)
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: NullSerializer)
end
def [](name)
if encrypted_message = @parent_jar[name]
decrypt_and_verify(encrypted_message)
deserialize name, decrypt_and_verify(encrypted_message)
end
end
@ -452,7 +514,8 @@ def []=(name, options)
else
options = { :value => options }
end
options[:value] = @encryptor.encrypt_and_sign(options[:value])
options[:value] = @encryptor.encrypt_and_sign(serialize(name, options[:value]))
raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
@parent_jar[name] = options
@ -464,18 +527,6 @@ def decrypt_and_verify(encrypted_message)
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
nil
end
def serializer
serializer = @options[:session_serializer] || :marshal
case serializer
when :marshal
ActionDispatch::Session::MarshalSerializer
when :json
ActionDispatch::Session::JsonSerializer
else
serializer
end
end
end
# UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore
@ -487,7 +538,7 @@ class UpgradeLegacyEncryptedCookieJar < EncryptedCookieJar #:nodoc:
def [](name)
if encrypted_or_signed_message = @parent_jar[name]
decrypt_and_verify(encrypted_or_signed_message) || verify_and_upgrade_legacy_signed_message(name, encrypted_or_signed_message)
deserialize(name, decrypt_and_verify(encrypted_or_signed_message)) || verify_and_upgrade_legacy_signed_message(name, encrypted_or_signed_message)
end
end
end

@ -1,3 +1,5 @@
require 'active_support/core_ext/hash/keys'
module ActionDispatch
class Request < Rack::Request
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
@ -50,13 +52,14 @@ def initialize(flash)
end
def []=(k, v)
k = k.to_s
@flash[k] = v
@flash.discard(k)
v
end
def [](k)
@flash[k]
@flash[k.to_s]
end
# Convenience accessor for <tt>flash.now[:alert]=</tt>.
@ -92,8 +95,8 @@ def to_session_value
end
def initialize(flashes = {}, discard = []) #:nodoc:
@discard = Set.new(discard)
@flashes = flashes
@discard = Set.new(stringify_array(discard))
@flashes = flashes.stringify_keys
@now = nil
end
@ -106,17 +109,18 @@ def initialize_copy(other)
end
def []=(k, v)
k = k.to_s
@discard.delete k
@flashes[k] = v
end
def [](k)
@flashes[k]
@flashes[k.to_s]
end
def update(h) #:nodoc:
@discard.subtract h.keys
@flashes.update h
@discard.subtract stringify_array(h.keys)
@flashes.update h.stringify_keys
self
end
@ -129,6 +133,7 @@ def key?(name)
end
def delete(key)
key = key.to_s
@discard.delete key
@flashes.delete key
self
@ -155,7 +160,7 @@ def each(&block)
def replace(h) #:nodoc:
@discard.clear
@flashes.replace h
@flashes.replace h.stringify_keys
self
end
@ -186,6 +191,7 @@ def now
# flash.keep # keeps the entire flash
# flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
def keep(k = nil)
k = k.to_s if k
@discard.subtract Array(k || keys)
k ? self[k] : self
end
@ -195,6 +201,7 @@ def keep(k = nil)
# flash.discard # discard the entire flash at the end of the current action
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
def discard(k = nil)
k = k.to_s if k
@discard.merge Array(k || keys)
k ? self[k] : self
end
@ -231,6 +238,12 @@ def notice=(message)
def now_is_loaded?
@now
end
def stringify_array(array)
array.map do |item|
item.kind_of?(Symbol) ? item.to_s : item
end
end
end
def initialize(app)

@ -1,13 +0,0 @@
module ActionDispatch
module Session
class JsonSerializer
def self.load(value)
JSON.parse(value, quirks_mode: true)
end
def self.dump(value)
JSON.generate(value, quirks_mode: true)
end
end
end
end

@ -1,14 +0,0 @@
module ActionDispatch
module Session
class MarshalSerializer
def self.load(value)
Marshal.load(value)
end
def self.dump(value)
Marshal.dump(value)
end
end
end
end

@ -67,6 +67,16 @@ def test_from_session_value
assert_equal({'flashes' => {'message' => 'Hello'}, 'discard' => %w[message]}, hash.to_session_value)
end
def test_from_session_value_on_json_serializer
decrypted_data = "{ \"session_id\":\"d98bdf6d129618fc2548c354c161cfb5\", \"flash\":{\"discard\":[], \"flashes\":{\"message\":\"hey you\"}} }"
session = ActionDispatch::Cookies::JsonSerializer.load(decrypted_data)
hash = Flash::FlashHash.from_session_value(session['flash'])
assert_equal({'discard' => %w[message], 'flashes' => { 'message' => 'hey you'}}, hash.to_session_value)
assert_equal "hey you", hash[:message]
assert_equal "hey you", hash["message"]
end
def test_empty?
assert @hash.empty?
@hash['zomg'] = 'bears'

@ -175,13 +175,13 @@ def test_keep_and_discard_return_values
assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed
assert_nil flash.discard(:unknown) # non existent key passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard().to_hash) # nothing passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed
assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.discard().to_hash) # nothing passed
assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed
assert_equal(:foo_indeed, flash.keep(:foo)) # valid key passed
assert_nil flash.keep(:unknown) # non existent key passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep().to_hash) # nothing passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed
assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.keep().to_hash) # nothing passed
assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed
end
def test_redirect_to_with_alert

@ -11,6 +11,16 @@
require 'active_support/message_verifier'
class CookiesTest < ActionController::TestCase
class CustomSerializer
def self.load(value)
value.to_s + " and loaded"
end
def self.dump(value)
value.to_s + " was dumped"
end
end
class TestController < ActionController::Base
def authenticate
cookies["user_name"] = "david"
@ -359,9 +369,72 @@ def test_read_permanent_cookie
assert_equal 'Jamie', @controller.send(:cookies).permanent[:user_name]
end
def test_signed_cookie
def test_signed_cookie_using_default_serializer
get :set_signed_cookie
assert_equal 45, @controller.send(:cookies).signed[:user_id]
cookies = @controller.send :cookies
assert_not_equal 45, cookies[:user_id]
assert_equal 45, cookies.signed[:user_id]
end
def test_signed_cookie_using_marshal_serializer
@request.env["action_dispatch.cookies_serializer"] = :marshal
get :set_signed_cookie
cookies = @controller.send :cookies
assert_not_equal 45, cookies[:user_id]
assert_equal 45, cookies.signed[:user_id]
end
def test_signed_cookie_using_json_serializer
@request.env["action_dispatch.cookies_serializer"] = :json
get :set_signed_cookie
cookies = @controller.send :cookies
assert_not_equal 45, cookies[:user_id]
assert_equal 45, cookies.signed[:user_id]
end
def test_signed_cookie_using_custom_serializer
@request.env["action_dispatch.cookies_serializer"] = CustomSerializer
get :set_signed_cookie
assert_not_equal 45, cookies[:user_id]
assert_equal '45 was dumped and loaded', cookies.signed[:user_id]
end
def test_signed_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json
@request.env["action_dispatch.cookies_serializer"] = :hybrid
key_generator = @request.env["action_dispatch.key_generator"]
signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"]
secret = key_generator.generate_key(signed_cookie_salt)
marshal_value = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal).generate(45)
@request.headers["Cookie"] = "user_id=#{marshal_value}"
get :get_signed_cookie
cookies = @controller.send :cookies
assert_not_equal 45, cookies[:user_id]
assert_equal 45, cookies.signed[:user_id]
verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON)
assert_equal 45, verifier.verify(@response.cookies['user_id'])
end
def test_signed_cookie_using_hybrid_serializer_can_read_from_json_dumped_value
@request.env["action_dispatch.cookies_serializer"] = :hybrid
key_generator = @request.env["action_dispatch.key_generator"]
signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"]
secret = key_generator.generate_key(signed_cookie_salt)
json_value = ActiveSupport::MessageVerifier.new(secret, serializer: JSON).generate(45)
@request.headers["Cookie"] = "user_id=#{json_value}"
get :get_signed_cookie
cookies = @controller.send :cookies
assert_not_equal 45, cookies[:user_id]
assert_equal 45, cookies.signed[:user_id]
assert_nil @response.cookies["user_id"]
end
def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signature
@ -369,7 +442,18 @@ def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signatu
assert_nil @controller.send(:cookies).signed[:non_existant_attribute]
end
def test_encrypted_cookie
def test_encrypted_cookie_using_default_serializer
get :set_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal 'bar', cookies[:foo]
assert_raise TypeError do
cookies.signed[:foo]
end
assert_equal 'bar', cookies.encrypted[:foo]
end
def test_encrypted_cookie_using_marshal_serializer
@request.env["action_dispatch.cookies_serializer"] = :marshal
get :set_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal 'bar', cookies[:foo]
@ -379,33 +463,66 @@ def test_encrypted_cookie
assert_equal 'bar', cookies.encrypted[:foo]
end
class CustomJsonSerializer
def self.load(value)
JSON.load(value) + " and loaded"
end
def self.dump(value)
JSON.dump(value + " was dumped")
end
end
def test_encrypted_cookie_using_serializer_object
@request.env["action_dispatch.session_serializer"] = CustomJsonSerializer
get :set_encrypted_cookie
assert_equal 'bar was dumped and loaded', cookies.encrypted[:foo]
end
def test_encrypted_cookie_using_json_serializer
@request.env["action_dispatch.session_serializer"] = :json
@request.env["action_dispatch.cookies_serializer"] = :json
get :set_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal 'bar', cookies[:foo]
assert_raises TypeError do
assert_raises ::JSON::ParserError do
cookies.signed[:foo]
end
assert_equal 'bar', cookies.encrypted[:foo]
end
def test_encrypted_cookie_using_custom_serializer
@request.env["action_dispatch.cookies_serializer"] = CustomSerializer
get :set_encrypted_cookie
assert_not_equal 'bar', cookies.encrypted[:foo]
assert_equal 'bar was dumped and loaded', cookies.encrypted[:foo]
end
def test_encrypted_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json
@request.env["action_dispatch.cookies_serializer"] = :hybrid
key_generator = @request.env["action_dispatch.key_generator"]
encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
secret = key_generator.generate_key(encrypted_cookie_salt)
sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
marshal_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: Marshal).encrypt_and_sign("bar")
@request.headers["Cookie"] = "foo=#{marshal_value}"
get :get_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal "bar", cookies[:foo]
assert_equal "bar", cookies.encrypted[:foo]
encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON)
assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"])
end
def test_encrypted_cookie_using_hybrid_serializer_can_read_from_json_dumped_value
@request.env["action_dispatch.cookies_serializer"] = :hybrid
key_generator = @request.env["action_dispatch.key_generator"]
encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"]
encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"]
secret = key_generator.generate_key(encrypted_cookie_salt)
sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)
json_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON).encrypt_and_sign("bar")
@request.headers["Cookie"] = "foo=#{json_value}"
get :get_encrypted_cookie
cookies = @controller.send :cookies
assert_not_equal "bar", cookies[:foo]
assert_equal "bar", cookies.encrypted[:foo]
assert_nil @response.cookies["foo"]
end
def test_accessing_nonexistant_encrypted_cookie_should_not_raise_invalid_message
get :set_encrypted_cookie
assert_nil @controller.send(:cookies).encrypted[:non_existant_attribute]
@ -721,8 +838,6 @@ def test_cookies_hash_is_indifferent_access
assert_equal "dhh", cookies['user_name']
end
def test_setting_request_cookies_is_indifferent_access
cookies.clear
cookies[:user_name] = "andrew"

@ -346,10 +346,8 @@ for detailed changes.
params "deep munging" that was used to address security vulnerability
CVE-2013-0155. ([Pull Request](https://github.com/rails/rails/pull/13188))
* Added `:serializer` option for `config.session_store :cookie_store`. This
changes default serializer when using
`:cookie_store`. ([Pull Request](https://github.com/rails/rails/pull/13692))
* New config option `config.action_dispatch.cookies_serializer` for specifying
a serializer for the signed and encrypted cookie jars. (Pull Requests [1](https://github.com/rails/rails/pull/13692), [2](https://github.com/rails/rails/pull/13945) / [More Details](upgrading_ruby_on_rails.html#cookies-serializer))
Action Mailer
-------------

@ -381,22 +381,6 @@ You can also pass a `:domain` key and specify the domain name for the cookie:
YourApp::Application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com"
```
You can pass `:serializer` key to specify serializer for serializing session:
```ruby
YourApp::Application.config.session_store :cookie_store, key: '_your_app_session', serializer: :json
```
The default serializer for new application is `:json`. For compatibility with
old applications `:marshal` is used when `serializer` option is not specified.
It is also possible to pass a custom serializer class with `load` and `dump`
public methods defined:
```ruby
YourApp::Application.config.session_store :cookie_store, key: '_your_app_session', serializer: MyCustomSerializer
```
Rails sets up (for the CookieStore) a secret key used for signing the session data. This can be changed in `config/initializers/secret_token.rb`
```ruby
@ -588,6 +572,38 @@ end
Note that while for session values you set the key to `nil`, to delete a cookie value you should use `cookies.delete(:key)`.
Rails also provides a signed cookie jar and an encrypted cookie jar for storing
sensitive data. The signed cookie jar appends a cryptographic signature on the
cookie values to protect their integrity. The encrypted cookie jar encrypts the
values in addition to signing them, so that they cannot be read by the end user.
Refer to the [API documentation](http://api.rubyonrails.org/classes/ActionDispatch/Cookies.html)
for more details.
These special cookie jars use a serializer to serialize the assigned values into
strings and deserializes them into Ruby objects on read.
You can specify what serializer to use:
```ruby
Rails.application.config.action_dispatch.cookies_serializer = :json
```
The default serializer for new applications is `:json`. For compatibility with
old applications with existing cookies, `:marshal` is used when `serializer`
option is not specified.
You may also set this option to `:hybrid`, in which case Rails would transparently
deserialize existing (`Marshal`-serialized) cookies on read and re-write them in
the `JSON` format. This is useful for migrating existing applications to the
`:json` serializer.
It is also possible to pass a custom serializer that responds to `load` and
`dump`:
```ruby
Rails.application.config.action_dispatch.cookies_serializer = MyCustomSerializer
```
Rendering XML and JSON data
---------------------------

@ -98,6 +98,19 @@ If your test helper contains a call to
is now done automatically when you `require 'test_help'`, although
leaving this line in your helper is not harmful in any way.
### Cookies serializer
Applications created before Rails 4.1 uses `Marshal` to serialize cookie values into
the signed and encrypted cookie jars. If you want to use the new `JSON`-based format
in your application, you can add an initializer file with the following content:
```ruby
Rails.application.config.cookies_serializer :hybrid
```
This would transparently migrate your existing `Marshal`-serialized cookies into the
new `JSON`-based format.
### Changes in JSON handling
There are a few major changes related to JSON handling in Rails 4.1.

@ -206,7 +206,7 @@ def env_config
"action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt,
"action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt,
"action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt,
"action_dispatch.session_serializer" => config.session_options[:serializer]
"action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer
})
end
end

@ -0,0 +1,3 @@
# Be sure to restart your server when you modify this file.
Rails.application.config.action_dispatch.cookies_serializer = :json

@ -1,3 +1,3 @@
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: <%= "'_#{app_name}_session'" %>, serializer: :json
Rails.application.config.session_store :cookie_store, key: <%= "'_#{app_name}_session'" %>

@ -433,7 +433,7 @@ def test_no_active_record_or_test_unit_if_skips_given
def test_new_hash_style
run_generator [destination_root]
assert_file "config/initializers/session_store.rb" do |file|
assert_match(/config.session_store :cookie_store, key: '_.+_session', serializer: :json/, file)
assert_match(/config.session_store :cookie_store, key: '_.+_session'/, file)
end
end