I've been learning web development with React and Ruby on Rails, and decided to share some of what I've learned about creating cookies for your site. Let's get right to it.
Intro to Cookies
Cookies are often a vital part of a user's experience of the web- they enhance convenience and allow the user to have a more customized experience on a site. They can be used to persist a user's login so that they don't have to login again when they refresh the page; they can also be used to store user preferences and settings, keep track of items in a shopping cart, track user behavior for analytical purposes, and target personalized ads to the user, among other things.
So what exactly are cookies, and how do they work?
Cookies are actually small pieces of data that are stored on the user's computer by a web browser while a user is browsing a website. When a user visits a website, the browser sends a request to the server. The server's response can include a Set-Cookie
header which will include the cookie's name, value, and attributes such as expiration date, or security flags. The browser then stores this cookie in a small text file on the user's computer. Then, as the user navigates the site, sending HTTP requests to the server, the browser automatically includes all the associated cookies in the request. Each cookie is associated with a specific domain, and the browser ensures that cookies are only sent back to the domain that sent them. Then, in the backend, the server reads the cookie data from the request and uses the information to identify the user and apply their preferences, among other tasks.
Main types of cookies
Session Cookies
There are a few different types of cookies that a developer can use on their site. The first is a session cookie; these cookies are temporary and expire after the browser is closed, but are useful for data that's needed for a single browsing session. Here's how you would make a session cookie to store a user's login info in Rails:
class SessionsController < ApplicationController
# '/login' routes to the create method in SessionsController
def create
# Find the user in the database using the given username
# when they attempted to login
user = User.find_by(username: params[:username])
# If the user exists and they gave the correct password,
# store the user id in the session to persist login
if user&.authenticate(params[:password])
session[:user_id] = user.id
render json: user, status: :created
else
render json: {error: "Unauthorized"},
status: :unauthorized
end
end
end
It's really that simple to create a session cookie! Now, even when the user refreshes a page on the website, the frontend can request relevant settings and data for the user that is logged in, and the backend will then check if the given user is actually logged in and, if so, return the requested information. In this example, if the user gives an incorrect username or password, we instead render an error message to let the frontend know that the login failed or there is no user logged in.
If you tried to recreate this example in your own project and it didn't work, you'll need to make sure you have the Active Record and BCrypt gems installed in your project, and to add the code has_secure_password
to your user.rb model.
Persistent Cookies
Making a persistent cookie is also easy! It's important to note that you should also clear persistent cookies when the user logs out, which is normally handled in destroy
in the SessionsController
. Let's look at the same situation, storing the user_id
to a cookie upon login, but with persistent cookies instead:
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
cookies.permanent[:user_id] = user.id
render json: user, status: :created
else
...
end
end
The .permanent
method on cookies
will automatically set the cookie to expire in 20 years. To specify an expiration, you would use a cookie without .permanent
and specify a duration, such as:
cookies[:user_id] = { value: user.id, expires: 2.weeks.from_now }
You can replace 2.weeks.from_now
with any other ActiveSupport::Duration method to suit your specific needs.
To clear the cookie, you would have this destroy method in the sessions controller:
def destroy
cookies.delete(:user_id)
end
Easy peasy!
Zombie Cookies
Zombie cookies are so called because they are usually stored in multiple locations and will recreate themselves even after the user deletes all their stored cookies. For this reason, zombie cookies are controversial, and their use is seen as a way to circumvent a users preferences relating to privacy and cookie management.
Cookie security and best practices
Because cookies can be used for nefarious purposes, it's best to follow standard best practices when using cookies on our own sites. Keeping up with security can help protect your users, so it's of vital importance when creating cookies! For example, for our server-side cookie we set earlier, we could do the following to make them more secure:
class SessionsController < ApplicationController
def create
...
cookies.signed[:user_id] = {
value: user.id,
httponly: true,
secure: Rails.env.production?
}
...
end
end
In our create method, we create a cookie using cookies.signed, for extra security. We then set httponly to true, which prevents access to cookie data via Javascript. The secure flag, which we set to be true only if the environment is in a production setting, means that cookies can only be sent over HTTPS connections. We keep this flag set to false in a development environment, as cookies will not be set when secure is true in development since it is not using HTTPS.
Another very important piece of security for cookies is to not store any sensitive data in the cookie itself, but rather session identifiers.
I highly recommend checking the Rails Security Guide, for a more comprehensive overview of how to ensure your cookies are secure in your rails backend and can't be used to harm or steal information from your users.
For more information on creating cookies in a Rails application, please visit the official documentation on cookies here.
If you have any feedback, suggestions, or noticed any errors in this post, please leave a comment letting me know! Thanks for reading, I hope it helps.
Top comments (0)