Cookies & Sessions in Rails
Tags: rails, postgres, codenewbie, tutorial
Published: 2022-07-31 00:34:52


![Photo by <a href="https://unsplash.com/@sjcbrn?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">SJ .</a> on <a href="https://unsplash.com/s/photos/cookies?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
  ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0n7l4fndpvoeagzl7j1.png)<a id="top"></a>
## What are Cookies & Sessions in Rails?
I've learned a lot about cookies & sessions while creating my Phase 4 React/Rails project at [Flatiron School](https://flatironschool.com), and I'm excited to share some of it with you.

This article aims to explain what [cookies](https://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Cookies.html) & [sessions](https://guides.rubyonrails.org/security.html#sessions) are in regard to the [request/response](https://en.wikipedia.org/wiki/Request%E2%80%93response) cycle, what they help us do, and how to configure a Rails API so you can use them in your application. I've included many links to resources throughout this reading if there are any terms you'd like to learn more about.

### Table of Contents
* [What are Cookies?](#cookies)
* [What are Sessions?](#sessions)
* [How to start building a Rails API that uses cookies & sessions](#how_to)
    * [File Structure](#files)
    * [Middleware](#middle)
    * [Include Cookies in ApplicationController](#include)
* [Conclusion and Next Steps](#conclusion)

## Adding State to an API Request
[HTTP](https://www.cloudflare.com/learning/ddos/glossary/hypertext-transfer-protocol-http/) is a [stateless](https://www.interviewbit.com/blog/stateful-vs-stateless/) protocol. HTTP servers receive and process requests, return data, then forgets about it.
Cookies help us (the client) make stateful HTTP requests by letting the server remember us with the information it stores.

## What are cookies?<a id="cookies"></a>
[(back to top)](#top)


[Cookies](https://www.rubydoc.info/docs/rails/3.2.8/ActionDispatch/Cookies) are collections of data, usually key:value pairs, that are created by a web server when someone asks for access to their website. When the server computes what it needs to and sends a response, it also sends cookies in HTTP headers to the client, and are stored by the user's browser on the hard drive.

Cookies are:
* Small: no more than 4kb 
* Not complex: a collection of name:value pairs, not programs or models
* Domain-specific: other websites have no way of reading them
* Temporary by nature: they can expire, or get updated frequently.

Cookies allow a user to persist data across requests, which is used for a [variety of UI needs](https://techcommunity.microsoft.com/t5/iis-support-blog/session-state-and-session-cookies-best-practices/ba-p/714333). Cookies keep us on the same page, in more ways than one.

Examples of what cookies might [keep track of](https://homepage.cs.uri.edu/faculty/wolfe/book/Readings/R09%20Links/cookie.htm) are:
* An expiration date/time for automatic logout, requiring a new login, creating a new session & new set of cookies
* Pages within a site you've visited
* Letting the server know if you're a first-time user
* Your flight itinerary
* Previously inputted contact information
* Your zip code for local weather information
* Remembering what's in your shopping cart so you can sleep on it...

Let's say a user goes to a website they've been to before, like the one we're on right now. 
* When you type ``https://dev.to/`` into your browser and hit ``enter``, your browser begins to send a **request** to the Web server for the page. First, the browser looks on your hard drive for a cookie file that ``dev.to`` may have set. If it finds a cookie, it will send the encrypted cookie data along with the URL to ``dev.to``'s server. 

* The server for ``dev.to`` will decrypt those cookies (if any), and send back a **response** to the client with program files that may apply uniquely to that user. For example, my cookies allow me to view [``https://dev.to/``](https://dev.to) in dark mode by default when I go to their website.

If you'd like to take a peek at your own cookies, pull up your browser's [Dev tools](https://developer.chrome.com/docs/devtools/storage/cookies/) (right-click then -> inspect), choose the Application tab, and check out the cookies you have for the website you're on. 

![Screenshot of Chrome Dev Tools showing cookies data in the Application tab](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/80xmalv59xasxb1h61ps.png)

You might see a long string of random characters, numbers, and symbols that you won't be able to read, and if you try to change them (not recommended), the website's server won't accept them anymore. You won't break anything, but you might have to reload your screen.

Here's an example of what a random cookie from my browser looks right right now:
```
# Cookie name:value
__cf_bm: x1MTF2K9gphl51GldO4Dq1TxBY5CXFO0YRXs9_zQjTc-1659208596-0-AfIDBDqPcHBuY2y1l3pO3jQaoORPrRrlw5NJP0flJJdPyOZQvKWS8loytli0XA0DNGuUE+jdyvcOGF8o4R9+23Bf1dM8SHGkspE8Sfsmeq3G
```
This is pretty hard to read because it's been encrypted & signed by Rails. By the time anyone (or thing) thinks to figure out what it means, the server won't remember what it means. Let's find out why that is.



![![Photo by <a href="https://unsplash.com/@thalia_s_ruiz?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Thalia Ruiz</a> on <a href="https://unsplash.com/s/photos/cookies?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
  ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zyziethtykjevzrrrhdk.png)
 ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d2rnfocyglybybqe8to3.png)
## What are sessions?<a id="sessions"></a>
[(back to top)](#top)

So how are cookies secure? I know one solution: Rails to the rescue!

Instead of sending cookies in plain text, we can use Rails to sign a special cookie only it knows how to unscramble known as a [session](https://guides.rubyonrails.org/action_controller_overview.html#session) using the ``session`` method. Session can store any simple Ruby object, and behaves like a hash: 

``session[:user_id]``

Application information can be securely stored in the user's browser because sessions encrypts and signs those cookies.
* A Rails session stores a unique, [cryptographically signed & encrypted ID](https://en.wikipedia.org/wiki/Identity-based_encryption) (not the actual session_id, for security reasons) on a small block of data. The Rails app responsible can decrypt and read the contents of that cookie if it's sent back. 
* Sessions should be used to store all session data on the cookie that's sent to the user along with all of the other files their browser needs to run the app.
* When the user interacts with the app, the session will last as long as they're logged in, allowing them to do whatever they're [authorized](https://auth0.com/docs/get-started/identity-fundamentals/authentication-and-authorization) to do. 
* When they log out, the session "ends" and their cookie data is obsolete. Cookies live on your computer until you delete them.
* Sessions shouldn't be used to store highly confidential information, complex objects, or large amounts of data.

How you decide to use sessions to control your app's behavior is up to you.



![Photo by <a href="https://unsplash.com/@nate_dumlao?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Nathan Dumlao</a> on <a href="https://unsplash.com/s/photos/cookies?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
  ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qopigmxc53avdl385u18.png)
## How to start building a Rails API with Postgres that uses cookies & sessions<a id="how_to"></a>
[(back to top)](#top)

### **Setup**

If you'd like to try this out yourself, get a code editor like [VS Code](https://code.visualstudio.com/download), and make sure you have the following installed:
*     [Ruby 2.7.4](https://www.ruby-lang.org/en/documentation/installation/)
*     [Rails 7.0.3](https://guides.rubyonrails.org/v5.0/getting_started.html)
*     [Postgresql 14.4](https://www.postgresql.org/download/)

### **Create**

Once those have been installed, create a new Rails API from your command line:

``$ rails new example-app --api --database=postgresql``
*     You can name your app anything you want.
*     The ``--api`` flag means you're creating a Rails app with no View or ERB, just the modules to build a relational database using the M and the C in [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
*     To use a test-suite like Rspec, add a ``-T`` flag to the end if you want.

### **Explore**

Now, navigate to your new app 

``cd example-app`` 

and open the app in your code editor to begin the fun part with

``code .``

_____

### File Structure<a id="files"></a>
[(back to top)](#top)

The highlights of your file structure are:
* ``./app``, where the core functionality of the app lives, such as Models, Controllers, Views, and Serializers.
* ``./bin`` has built-in Rails tasks you probably won't need to worry about starting out.
* ``./config`` manages settings that control environment settings, modules that need to load on initialization, database settings, app routes, etc.
* ``./db`` holds migrations, seed data and the schema for your database.
* ``./lib`` holds custom Rake tasks.
* ``./log`` holds logs for debugging purposes.
* ``Gemfile`` contains all gems included with the application. This is where you can add your own gems.
* ``Gemfile.lock`` shouldn't be edited. This is created when you run ``bundle install`` and contains every Gem's dependencies & associated versions. If you need to, you can delete this file and run ``bundle install`` again to make sure you have the right gems installed according to your Gemfile.
* ``README.md`` documents the details of the application, and can (and should) be as thorough as you want.

_____

### Middleware<a id="middle"></a>
[(back to top)](#top)

Unfortunately, cookie middleware isn't automatically included in our Rails API. Without them, no cookies. Before we hop into sessions & cookies, we need to configure our rails application to use them.

Open ``./config/application.rb`` and add a couple pieces of middleware. Here's what it should look like:

![Application config file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/08q91q0vjpbi9kfkedol.png)
And here's what you need to add:
```
# config/application.rb
module MyApp
  class Application < Rails::Application
    config.load_defaults 6.1
    config.api_only = true

    # Must add these lines!
    config.middleware.use ActionDispatch::Cookies
    config.middleware.use ActionDispatch::Session::CookieStore

    # Use SameSite=Strict for all cookies to help protect against CSRF
    config.action_dispatch.cookies_same_site_protection = :strict
  end
end
```

[``config.middleware.use ActionDispatch::Cookies``](https://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack) 
* Lets us use cookies

[``config.middleware.use ActionDispatch::Session::CookieStore``](https://api.rubyonrails.org/classes/ActionDispatch/Session/CookieStore.html) 
* Gives us access to the methods in Rails' CookieStore

[``config.action_dispatch.cookies_same_site_protection = :strict``](https://web.dev/samesite-cookies-explained/) 
* This line adds a little security, only allowing us to accept cookies from applications running on the same domain as ours (like running a Rails & React project from the same domain)




_____
### Include Cookies in ApplicationController<a id="include"></a>
[(back to top)](#top)

Finally, in order to use this we need to configure our most parent controller, ApplicationController, and instruct it to use cookies so the rest of our controllers have access to them. At the top of the file, [include it](https://guides.rubyonrails.org/action_controller_overview.html#cookies
).
```
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include ActionController::Cookies
  ...
end
```


![Photo by <a href="https://unsplash.com/@erol?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Erol Ahmed</a> on <a href="https://unsplash.com/s/photos/cookies?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
  ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m07enrhwropmgasg1iia.png)

## In Conclusion<a id="conclusion"></a>
[(back to top)](#top)
Nice work! That's it for this step. Now your app can use sessions & cookies.

That was a lot! And there's so much more to learn, but there always is, and you're off to a good start. Take your time practicing, reading the docs, and navigating error messages. It's worth it, I promise.

From here you can run ``bundle install`` to install your gems, then ``rails s`` to start up your rails server. Then, you'll need to set up your [Users and Sessions controllers](https://flatironschool.com/blog/an-epic-adventure-how-to-create-sessions-in-rails/), and continue to build an app with the ["BCrypt" gem for authorization](https://medium.com/@ashleymcolletti/add-authentication-to-your-rails-app-with-bcrypt-a53917252159), create [associations](https://guides.rubyonrails.org/association_basics.html), [models](https://guides.rubyonrails.org/active_record_basics.html), [controllers](https://guides.rubyonrails.org/action_controller_overview.html), [serializers](https://medium.com/@maxfpowell/a-quick-intro-to-rails-serializers-b390ced1fce7), and [migrations](https://edgeguides.rubyonrails.org/active_record_migrations.html). There's a lot to do still, but you can do it.

As parting friendly advice, do yourself a favor:

_Use a diagram app like [drawio](https://app.diagrams.net/) or [DB Diagram](https://dbdiagram.io/home), or a pencil & paper, and plan out your [MVP](https://blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp) before you code anything else!_

While it's not necessary, it's highly recommended to think through the purpose of your api, the table associations and how they will manage the database's data, password management, and what endpoints and routes you want the user to use from the frontend. 

Take it from me, _it will save you a lot of time_ if you plan your app from top to bottom before you start coding.

Thank you for reading this far, I hope it helps. If there's anything I missed, please feel free to add it in the comments. 

Happy coding!