Simple Login and Signup in Rails

Charlie Knight
4 min readJan 13, 2021

Most sites we visit in this day and age require some sort of login, or they don’t but it enhances the user experience if you make one. Using a basic email/password combination I will create a user and login, and apply principles from authentication, session and authorization in Rails.

To walk through the steps I’m going to make a new app and call it LoginApp, it will have two models; a User model and a Session model. Sessions will allow a user to navigate the site and be authorized to see certain content, content that is private to that specific user. All of their information will be stored in cookies and they won’t have to continue to log in each time they open a new page.

For the User model I will run:

rails g resource user username password_digest

Sessions I’m just going to make a controller:

rails g controller sessions

I’ll come back to Sessions after we create a new User. In the User controller I will have a strong params and session set to a user id. Session is a great built-in method to store user data for a dynamic site and allows for quick and easy authorization. Using it for logins/signups the user id will be stored as the session’s unique identifier. The user controller and view will look something like the following.

User Controller:

class UsersController < ApplicationControllerdef show
@user
= User.find(params[:id])
end
def new
@user
= User.new
end
def create
@user
= User.create(user_params)
if @user.valid?
session[:user_id] = @user.id
redirect_to @user
else
flash[:error] = "Not a valid username or password"
end
end
def user_params
params.require(:user).permit(:username, :password)
end
end

New User View / Signup:

#views/users/new.html.erb<%= form_with model: @user do |f| %>
<%=f.label "Username"%>
<%=f.text_field :username%><br>
<%=f.label "Password"%>
<%=f.text_field :password%><br>
<%=
f.submit%>
<%end%>
<%=flash[:error]%>

You’ll notice I checked if the user was valid on creation, I set some very simple validations on my user model. The username has to be unique and the password has to be between a certain number of characters, very common for user creation. Note you can also do your password withoutpassword_digest when creating the table but you will lose out on some of the inherent authentication functions.

User Validations:

class User < ApplicationRecord
validates :username, presence: true, uniqueness: true
validates :password, length: { in: 6..12 }
has_secure_password
end
Simple signup

make sure you have bcrpyt installed in your app. This enables me to use the secure_password method on the User class for authentication.

#Use Active Model has_secure_password
gem 'bcrypt', '~> 3.1.7'

Session / Login:

Now that we can create a new user we need to set up sessions so they can have user specific experience. There are a few different ways to set up a login page but we’ll do it through a session and then some work with the application controller. In the session controller we will use the following code:

class SessionsController < ApplicationController def new  end  def create
@user = User.find_by(username: params[:username])
if @user && @user.authenticate(params[:password])
session[:user_id] = @user.id
redirect_to user_path(@user.id)
else
flash[:alert] = “Invalid email or password”
render “new”
end
end

def destroy
session[:user_id] = nil
redirect_to root_path
end

I am setting it to go to the user’s page if they pass the creation of the session but this can be set to a welcome page or any other one of your liking. Authenticate is doing the following code:

class User < ActiveRecord::Base
has_secure_password validations: false
end

user = User.new(name: 'david', password: 'mUc3m00RsqyRe')
user.save
user.authenticate('notright') # => false
user.authenticate('mUc3m00RsqyRe') # => user

source: https://apidock.com/rails/ActiveModel/SecurePassword/InstanceMethodsOnActivation/authenticate

and is very helpful for passwords, since the return value is the instance of the user.

Adding Authorization

To add functionality for limiting what users can see is where you would add authorization. This is most commonly done int the ApplicationController since it will be applied across the entire application. The most common method/line of code you will see is the below:

class ApplicationController < ActionController::Base before_action :authorized
helper_method :current_user
helper_method :logged_in?
def current_user
User.find_by(id: session[:user_id])
end
def logged_in?
!current_user.nil?
end
def authorized
redirect_to '/login' unless logged_in?
end
end

the before_action will mean that any page across the application will require someone being logged in unless there is an action specifically stating not to. For our new/create and homepage we will not want any authorization. To skip authorization on these pages (and any others) we simply put in

#UsersControllerskip_before_action :authorized, only: [:new, :create] 

#any other routes you'd like to skip

Make sure to test out the authorization to see if it is working! Also make sure your routes are set up correctly, I had mine as such:

resources :usersget ‘/login’, to: ‘sessions#new’, as: '/login'post ‘/login’, to: ‘sessions#create’

It would probably be wise to set up routes to navigate to new users as a signup or welcome page so you’re using more implied language.

Sources:

https://auth0.com/blog/hashing-in-action-understanding-bcrypt/

--

--