RailsCompatibleCookiesUtils - Utility methods to deal with cookies managed by a Rails app

This is a Ruby-only (2.0 or later) implementation of utility methods to manage cookies generated by a Rails app. It doesn't depend on any external gems.

Just tell RailsCompatibleCookiesUtils the Rails app secret key and it will be able to handle signed and encrypted cookies (it can both read and write those values).

This is useful if you intend to share your cookies (or session) among different Ruby web applications (they could be even running in the same domain behind a proxy for example) when at least one of the applications is not a Rails one.

Or you could use this if you are switching from Rails to another Ruby web framework and want to deal with old valid sessions after deploying (either if it's a temporary strategy or you want to keep this encrypting and signing algorithms permanently).

Installation

Add this line to your application's Gemfile:

gem 'rails_compatible_cookies_utils'

And then execute:

$ bundle

Or install it yourself as:

$ gem install rails_compatible_cookies_utils

Usage

You may test the following examples in a REPL with bin/console.

Initialization

Creating an instance using the default serializer in Rails 5 generated applications (JSON):

cookies_utils = RailsCompatibleCookiesUtils.new 'secret_key_base'

Usually you'll pass the Rails app secret_key_base (Rails.application.config.secret_key_base) you want to share cookies with. You can also generate a Rails-like random secret key for other purposes if you want:

secret_key_base = SecureRandom.hex 64

In previous Rails releases the default serializer was Marshal. If your app uses it, initialize it this way:

cookies_utils = RailsCompatibleCookiesUtils.new 'secret_key_base', serializer: Marshal

Thread-safety

An instance of RailsCompatibleCookiesUtils is not thread-safe, so ensure to create a new instance for each thread using such an instance or make the calls in within a mutex synchronized block.

This would be the equivalent of cookies.encrypted['_myapp_session']:

raw_cookie_string = env['HTTP_COOKIE'] # this would be considering a Rack app
cookies_utils.decrypt_cookie_key raw_cookie_string, '_myapp_session'

Or if you already have the parsed and unescaped cookie value:

value = cookies_utils.cookies(env['HTTP_COOKIE'])['_myapp_session']
# you may actually get the value from somewhere else
cookies_utils.decrypt value

Write an encrypted value to the cookies

require 'cgi'
value = { test: true }
cookie = CGI::Cookie.new 'some_key', cookies_utils.encrypt value
# or using a framework helper:
# cookies['some_key'] = cookies_utils.encrypt value

cookies_utils.decrypt(cookies_utils.encrypt 'secret') == 'secret'

Signed only values

Rails also allows you to just sign a cookie value without encrypting it (cookies.signed['key'] = 'value'). To verify and decode the value:

cookies_utils.signed_cookie_key env['HTTP_COOKIE'], 'some_key'
# or cookies_utils.verify_and_deserialize signed_serialized_value

Serialize and sign a cookie value with:

require 'cgi'
value = { test: true }
cookie = CGI::Cookie.new 'some_key', cookies_utils.serialize_and_sign value
# or using a framework helper:
# cookies['some_key'] = cookies_utils.serialize_and_sign value

cookies_utils.verify_and_deserialize(cookies_utils.serialize_and_sign 'secret') == 'secret'

Invalid values handling

If the value is invalid decrypt_cookie_key, decrypt, signed_cookie_key, verify_and_deserialize and serialize_and_sign will all return nil. All of them have a bang variant which will raise RailsCompatibleCookiesUtils::InvalidSignature instead in those cases (like decrypt!).

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub.

License

The gem is available as open source under the terms of the MIT License.