Last week, I broke down what is browser storage, and today I'll be covering using cookies to log in. I'll be using specific examples from rails with a walk through on implementation. You can read the browser storage post here!
Cookies Refresher
Cookies are a way for websites to store data on your browser. More specifically, when you make a http request to a server, you share the site's specific cookies in the request. The server can send a response to the user telling them what to store as a cookie. This data is used to allow you to stay logged in between sessions, or maintain a cart on shopping site.
Rails Setup
To implement authentication and login using Rails, we can use some built in features. Rails has a built in method called has_secure_password that will allow you to store a password digest in your database, and handle the encryption. By default, has_secure_password uses the gem bcrypt for the encryption protocol. You will need to add this line to your gem file ( or un-comment it)
gem 'bcrypt', '~> 3.1.7'
Additionally, for your users table, rather than storing a password in the database, you will need a column on your table called password_digest. You can add a migration to change this on an existing rails app, or you can include it in your initial migrations if you are starting a new one. You do not need a 'password' column on your table, just the password_digest.
Once you have your migrations and gem file up to date, you can now update your user model to associate the password_digest to the has_secure_password method. Your user model should look something like this:
class User < ApplicationRecord
validates :username, presence: true
has_secure_password
end
Adding the has_secure_password line will add a bunch of methods to your User class, allowing you to store encrypted passwords, and check login credentials.
Application and Session Controllers
A session in rails is a built in way to access and change cookies pertaining to a user's logged in session. It abstracts the cookies away into acting like a ruby hash. We will be implementing a new controller to manage the cookies we save for maintaining a user's session. This is called the session controller.
When we think about the actions we need to take to save a user's data, we will need a way to check the cookies when someone accesses a restricted page, add a new cookie when someone logs in, and delete a cookie when someone logs out.
Fist in our ApplicationController, we can have a check to see if in the http request, the cookie or session includes a valid logged in user. We can use this current_user method across all of our controllers now to check who is logged in. The sessions are available to us using hash notation in ruby.
class ApplicationController < ActionController::Base
helper_method :current_user
def current_user
if session[:user_id]
User.find_by(id: session[:user_id])
end
end
end
In our new SessionsController, we can include a create which will correlate with logging in, and a destroy that will correlate with logging out.
class SessionsController < ApplicationController
def create
@user = User.find_by(username: params[:username])
if @user && @user.authenticate(params[:password])
session[:user_id] = @user.id
redirect_to @user
else
flash[:error] = "Invalid username or password."
redirect_to "/login"
end
end
def destroy
session[:user_id] = nil
redirect_to "/"
end
end
At this point, you need to make sure that your other controllers have the correct access on them, only allowing users that are logged in when necessary.
before_action :require_login
skip_before_action :require_login, only: [:create, :new]
def show
@user = User.find(params[:id])
end
def new
if current_user
redirect_to "/users/#{current_user.id}"
else
@user = User.new
end
end
def create
@user = User.new(user_params)
if @user.valid?
@user.save
session[:user_id] = @user.id
redirect_to @user
else
flash[:errors] = @user.errors.full_messages
redirect_to "/signup"
end
end
private
def user_params
params.require(:user).permit(:username, :first_name, :last_name, :password, :password_confirmation)
end
def require_login
return head(:forbidden) unless session.include? :user_id
end
You'll need to make sure that your routes match what you have setup, with your login and logout post requests mapping to the sessions controller. Additionally, this method works fine for small scale applications and projects, but for larger scaling, or bigger cookies, there can be issues with using cookies.
Next week I'll be looking at a specific user of local storage and JTW. Stay tuned!
Top comments (0)