In this article we teach you how to build a simple authentication system for your Rails 3.1 (and above) application.
First, we need to include the bcrypt ruby gem. Open your gemfile and find the line that says:
# gem 'bcrypt-ruby', '~> 3.0.0'
Uncomment the line. Next, run a bundler to install the gem:
This will install the BCrypt Ruby Gem, which will allow us to utilize hashed passwords.
Creating a User Model
We need to add a user model that we can authenticate against. Create a user model:
rails g model user email:string password_digest:string
Then run a rake db:migrate to update the database.
The password_digest field stores a salted hash of the user's password.
Now, before we dig in, lets create a a sample page that we want to protect with a username and password. Run the following command:
rails g controller Home show
This will create a controller called home, with an action called show. Next, open your routes file, remove the line from your routes file:
Now, add the following lines:
resource :home, only: [:show] root to: "home#show"
Our page to be protected is now created. Later, we will add the code to keep unauthenticated users from accessing this page.
Updating the User Model
Now that the database is squared away and we have a page to protect, we need to add a couple of lines of code to the model. Open your model. You will see something that looks similar to the following:
attr_accessible :email, :password_digest
Append the following code to the end of the line:
, :password, :password_confirmation
So that it looks something like this:
attr_accessible :email, :password_digest, :password, :password_confirmation
Next, add the following 2 lines of code.
has_secure_password validates_presence_of :password, :on => :create validates :email, uniqueness: true, presence: true
The first line tells Rails that we want to store and use a secure password. A few validations as well as a few methods are added. The second line adds a validation that checks for the presence of the password. The final line ensures that the email address specified is unique. Note that we don't actually check the formatting of the email address here, but you probably should. Validations will be covered in a later article.
One final thing, delete the public/index.html file so that Rails will take you to the correct site root.
Now we need to build a form that will allow the user to log in. Lets create a controller called sessions to do this:
rails g controller Sessions new create destroy
Next, open your routes.rb file and remove the following lines:
get "sessions/new" get "sessions/create" get "sessions/destroy"
Now add the following line:
resources :sessions, only: [:new, :create, :destroy]
Now open the sessions/new.html.erb view and add the following code for the login form.
<h1>Log in</h1> <p><%= flash[:notice] %></p> <%= form_for :session, url: sessions_path do |f| %> <div class="field"> <%= f.label :email %> <%= f.text_field :email %> </div> <div class="field"> <%= f.label :password %> <%= f.password_field :password %> </div> <div class="actions"><%= f.submit :login %></div> <% end %>
Next, we want to add some code to the controller to tell rails how to handle the form. Open the sessions_controller and add the following code for the create method:
user = User.find_by_email(params[:email]) if user && user.authenticate(params[:password]) session[:user_id] = user.id redirect_to root_url, :notice => "Logged in!" else flash[:notice] = "Invalid email or password" redirect_to new_session_path end
session[:user_id] = nil
This line of code will allow the user to log out.
Sign Up Form
Now, we need to create a sign up form where new users can sign up. First, lets create a new controller.
rails g controller users new create
This will create a Users controller that will allow users to sign up for an account. Next, open up your routes and remove the following lines:
get "users/new" get "users/create"
Then, add the following line to make the controller resourceful.
resources :users, only: [:new, :create]
This will set up our resourceful routes so that they work properly. Now, open up the users controller and add the following code to the new method:
@user = User.new
Also add the following code to the create method:
@user = User.new(params[:user]) if @user.save redirect_to root_url, notice: "Signed up!" else render :new end
Now that we have all of the code required allow the user to sign up, we need to build the forms. First, add the following code to your users/new.html.erb view.
<h1>Sign Up</h1> <%= form_for @user do |f| %> <% if @user.errors.any? %> <div class="error_messages"> <h2>Form is invalid</h2> <ul> <% for message in @user.errors.full_messages %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :email %> <%= f.text_field :email %> </div> <div class="field"> <%= f.label :password %> <%= f.password_field :password %> </div> <div class="field"> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation %> </div> <div class="actions"><%= f.submit "Sign Up" %></div> <% end %>
Our signup form is now complete and you can access it from /users/new. Now it's time to Add some finishing touches.
We are just about done, but first we need to add a few quick finishing touches. First, open your routes file and add the following routes:
get "login" => "sessions#new", :as => "login" get "sign_up" => "users#new", :as => "sign_up" get "logout" => "sessions#destroy", :as => "logout"
The first route will map /login to your login page, the second route will map /sign_up to your signup page, and the last route will map /logout to your logout page.
Next, open the ApplicationController and add the following code:
def current_user @current_user ||= User.find(session[:user_id]) if session[:user_id] end def require_valid_user if session[:user_id] == nil redirect_to login_path end end
The first method will return the current user if the user is logged in. The second method gets called by a page that needs a valid user. Finally, add the following code to your home controller:
This line causes the application to prompt the user to log in if he/she attempts to access any action on the home controller. That's it! You now have a working authentication system in Rails 3.1 and above.