8.1. Cookies

With a cookie, you can store information on the web browser's system, in form of strings as key-value pairs that the web server has previously sent to this browser. The information is later sent back from the browser to the server in the HTTP header. A cookie (if configured accordingly) is deleted from the browser system neither by restarting the browser nor by restarting the whole system. Of course, the browser's user can manually delete the cookie.

Important

A browser does not have to accept cookies and it does not have to save them either. But we live in a world where almost every page uses cookies. So most users will have enabled the cookie functionality. For more information on cookies, please visit Wikipedia at http://en.wikipedia.org/wiki/Http_cookie.

Tip

A cookie can only have a limited size (the maximum is 4 kB). You should remember that the information of the saved cookies is sent from the browser to the server. So you should only use cookies for storing small amounts of data (for example, a customer number) to avoid the protocol overhead becoming too big.
Rails provides a hash with the name cookies[] that we can use transparently. Rails automatically takes care of the technological details in the background.
To demonstrate how cookies work, we are going to build a Rails application that places a cookie on a page, reads it out on another page and displays the content, and the cookie is deleted on a third page.
$ rails new cookie_jar
  [...]
$ cd cookie_jar 
$ rails generate controller Home set_cookies show_cookies delete_cookies
      create  app/controllers/home_controller.rb
       route  get "home/delete_cookies"
       route  get "home/show_cookies"
       route  get "home/set_cookies"
      invoke  erb
      create    app/views/home
      create    app/views/home/set_cookies.html.erb
      create    app/views/home/show_cookies.html.erb
      create    app/views/home/delete_cookies.html.erb
      [...]
$ 
We populate the controller file app/controllers/home_controller.rb as follows:
class HomeController < ApplicationController
  def set_cookies
    cookies[:user_name]   = "Horst Meier" 
    cookies[:customer_number] = "1234567890" 
  end
  
  def show_cookies
    @user_name    = cookies[:user_name]
    @customer_number = cookies[:customer_number]
  end
  
  def delete_cookies
    cookies.delete :user_name
    cookies.delete :customer_number
  end
end
And the view file app/views/home/show_cookies.html.erb as follows:
<table>
 <tr><td>User Name:</td><td><%= @user_name %></td></tr>
 <tr><td>Customer Number:</td><td><%= @customer_number %></td></tr>
</table>
Start the Rails server with rails server and go to the URL http://0.0.0.0:3000/home/show_cookies in your browser. You will not see any values.
No cookies set
Now go to the URL http://0.0.0.0:3000/home/set_cookies and then back to http://0.0.0.0:3000/home/show_cookies. Now you will see the values that we have set in the method set_cookies.
Cookies set
By going to the page http://0.0.0.0:3000/home/delete_cookies you can delete the cookies again.

Important

The cookies you have placed in this way stay "alive" in the browser until you close the browser completely.

Warning

The content of a normally placed cookie in the browser is easy to read and does not pose any problems to the user. It is not encrypted, so it should not contain any passwords or similar data. Nor is it advisable to save shopping baskets in an unsigned (see the section called “Signed Cookies)”) cookie, otherwise the user could change the prices in this shopping basket himself.

Permanent Cookies

Cookies are normally set to give the application a way of recognizing users when they visit again later or of saving shopping baskets. Between these visits to the website, much time can go by and the user may well close the browser in the meantime. To store cookies for longer than the current browser session, you can use the method permanent. Our above example can be expanded by adding this method in the app/controllers/home_controller.rb:
class HomeController < ApplicationController
  def set_cookies
    cookies.permanent[:user_name]   = "Horst Meier" 
    cookies.permanent[:customer_number] = "1234567890" 
  end
  
  def show_cookies
    @user_name    = cookies[:user_name]
    @customer_number = cookies[:customer_number]
  end
  
  def delete_cookies
    cookies.delete :user_name
    cookies.delete :customer_number
  end
  
  def debug_cookies
  end
end

Important

"permanent" here does not really mean permanent. You cannot set a cookie permanently. When you set a cookie, it always needs a "valid until" stamp that the browser can use to automatically delete old cookies. With the method permanent this value is set to today's date in 20 years. This is practically the same as permanent.

Signed Cookies)

With normally placed cookies, you have no option on the application side to find out if the user of the application has changed the cookie. This can quickly lead to security problems, as changing the content of a cookie in the browser is no great mystery. The solution is signing the cookies with a key that is only known to us. This key is automatically created via a random generator with each rails new and is located in the file config/initializers/secret_token.rb:
# Be sure to restart your server when you modify this file.

# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
CookieJar::Application.config.secret_token = '85ec33910d4d57f3d3c69cb8fef20f158f68a219a76db71280779454a523330874fc4e42c7e62ecb25b8e4f5fcce1dcad88c7dcc9671a9922f675d770270a25a'

Warning

If you want to set up your Rails application from scratch again one day and start over with a fresh Rails project, it can be helpful to transfer this key into the new Rails application. Otherwise you can no longer use the previously placed cookies!
To sign cookies, you can use the method signed. You have to use it for writing and reading the cookie. Our above example can be expanded by adding this method in the app/controllers/home_controller.rb:
class HomeController < ApplicationController
  def set_cookies
    cookies.permanent.signed[:user_name]   = "Horst Meier" 
    cookies.permanent.signed[:customer_number] = "1234567890" 
  end

  def show_cookies
    @user_name    = cookies.signed[:user_name]
    @customer_number = cookies.signed[:customer_number]
  end

  def delete_cookies
    cookies.delete :user_name
    cookies.delete :customer_number
  end

  def debug_cookies
  end
end
The content of the cookie is now saved in encrypted form every time you set the cookie. The name of the cookie can still be read by the user, but not the value.

Updates about this book will be published on my Twitter feed.