#Simple Authentication with Bcrypt
This guide is for adding authentication to a vanilla Ruby on Rails app using Bcrypt and has_secure_password.
The steps below are based on Ryan Bates's approach from Railscast #250 Authentication from Scratch (revised).
You can see the final source code here: repo. I began with a stock rails app using rails new gif_vault
##Steps
-
Create a user model with a name, email and password_digest (all strings) by entering the following command into the command line:
rails generate model user name email password_digest.Note: If you already have a user model or you're going to use a different model for authentication, that model must have an attribute names password_digest and some kind of attribute to identify the user (like an email or a username).
-
Run
rake db:migratein the command line to migrate the database. -
Add these routes below to your routes.rb file. Notice I also deleted all the comments inside that file. Don't forget to leave the trailing
end, though.# config/routes.rb GifVault::Application.routes.draw do # This route sends requests to our naked url to the *cool* action in the *gif* controller. root to: 'gif#cool' # These routes will be for signup. The first renders a form in the browse, the second will # receive the form and create a user in our database using the data given to us by the user. get '/signup' => 'users#new' post '/users' => 'users#create' end
-
Create a users controller:
# app/controllers/users_controller.rb class UsersController < ApplicationController end
-
Add a new action (for rendering the signup form) and a create action (for receiving the form and creating a user with the form's parameters.):
# app/controllers/users_controller.rb class UsersController < ApplicationController def new end def create end end
-
Now create the view file where we put the signup form.
# app/views/users/new.html.erb <h1>Signup!</h1> <%= form_for :user, url: '/users' do |f| %> Name: <%= f.text_field :name %> Email: <%= f.text_field :email %> Password: <%= f.password_field :password %> Password Confirmation: <%= f.password_field :password_confirmation %> <%= f.submit "Submit" %> <% end %>
A note on Rail's conventions: This view file is for the new action of the users controller. As a result, we save the file here:
/app/views/users/new.html.erb. The file is called new.html.erb and it is saved inside the views folder, in a folder we created called users.That's the convention: view files are inside a folder with the same name as the controller and are named for the action they render.
-
Add logic to create action and create the private user_params method to sanitize the input from the form (this is a new Rails 4 thing and it's required).
class UsersController < ApplicationController
def new
end
def create
user = User.new(user_params)
if user.save
redirect_to '/'
else
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end ```
-
Go to the User model file and add
has_secure_password.# app/models/user.rb class User < ActiveRecord::Base has_secure_password end
-
Go to your Gemfile and uncomment the 'bcrypt' gem.
source 'https://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.0.4' # Use sqlite3 as the database for Active Record gem 'sqlite3' ... # Use ActiveModel has_secure_password gem 'bcrypt', '~> 3.1.7' ...
-
Run
bundle installfrom the terminal then restart your rails server. -
Create a sessions controller.
# app/controllers/sessions_controller.rb class SessionsController < ApplicationController def new end def create end def destroy end end
-
Create a form for user's to login with.
<!-- app/views/sessions/new.html.erb --> <h1>Login</h1> <%= form_tag '/login' do %> Email: <%= text_field_tag :email %> Password: <%= password_field_tag :password %> <%= submit_tag "Submit" %> <% end %>
-
Update your routes file to include new routes for the sessions controller.
GifVault::Application.routes.draw do root to: 'gif#cool' get '/login' => 'sessions#new' post '/login' => 'sessions#create' get '/logout' => 'sessions#destroy' get '/signup' => 'users#new' post '/users' => 'users#create' end
-
Update the sessions_controller with the logic to log users in and out.
# app/controllers/sessions_controller.rb def create user = User.find_by_email(params[:email]) # if the user exists AND the password entered is correct if user && user.authenticate(params[:password]) # save the user id inside the browser cookie. This is how we keep the user logged in when they navigate around our website. session[:user_id] = user.id redirect_to '/' else redirect_to '/login' end end def destroy session[:user_id] = nil redirect_to '/login' end
-
Update the application controller with new methods to set the current_user if cookies work and to authorize.
# app/controllers/application_controller.rb class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception def current_user current_user ||= User.find(session[:user_id]) if session[:user_id] end helper_method :current_user def authorize redirect_to '/login' unless current_user end end