HTTP is stateless meaning when request is made and server sends back the response, cycle is complete and know information about that cycle is remember.To help in storing information sessions.
With respect to RESTful resourcing we will make session a resource which will create during login so as the and destroy it so as to logout
Create the home page¶
Before we start our authentication let us quickly create landing page,it will the root of our application.
1 2 3
$ git checkout -b landing-page origin master $ git pull origin master $ rails g controller Statics home
Let us adjust our routes like so:
## truncated for brevity root to: 'statics#home
We create a simple page which allow to sign up like so:
1 2 3
<p>Welcome to mpesa app<p> <p><%= link_to "Sign up now!", new_user_path %><p>
Authentication involve the identification of a system, this is achieved by user telling us what who you are and what you know.Who you are in this should be unique to a particular user for instance email,KRA pin in our case it can be phone number.This usually in public domain after everybody knows.
What you know is something personal to you.Most cases it usually password.This usually private a combination of what you know and what you have can be used to identify a particular user.
To achieve this in our application user,user will provide her phone number and password which will compare with what we have in our database.
$ git checkout -b user-login origin/master
Let us generate session controller like so:
$ rails generate controller Sessions
If user details matches those in database we to create a session for her if not we should render login page with message
invalid phone number or password.
Adjust the routes like:
# truncated for brevity get '/login', to: 'sessions#new', as: 'login'
Adjust session controller to render login page/new session page like so:
1 2 3 4
class SessionsController < ApplicationController def new end end
Let us create the login page like so:
1 2 3 4 5 6 7 8 9 10 11 12 13
<%= form_with scope: :session, url: login_path, local: true do |f| %> <p> <%= f.label :phone_number %> <%= f.text_field :phone_number%></br> </p> <p> <%= f.label :password %> <%= f.password_field :password%></br> </p> <p><%= f.submit "Log in"%></p></br> <% end %> <p>New user? <%= link_to "Sign up now!", new_user_path %></p>
The page allow user to enter phone number and password and incase they are new users a link to signup.
Let us enable our users their data to the server for verification adding the
# truncated for brevity post '/login', 'sessions#create'
For us to creation to the user we must make sure phone number and password provide both match.
There we will phone number to check if user exist and password to confirm if user is who she say she is. app/controller/sessions_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13
# truncated for brevity def create user = User.find_by(phone_number: session_params[:phone_number]) if user && user.authenticate(session_params[:password]) else render 'new' end end private def session_params params.require(:session).permit(:phone_number, :password) end
In the above create action we find user by phone number provide this either returns
the user if phone number given belongs to an existing user or
nil if user is not available.
authenticate method is provide by the
has_secure_password,it compare the unecrypted version of password_digest with password provided by the user and return
true if they are the same or
For now if both find_by and authenticate return truthy value we do nothing but if either of them return falsy value we render login page.
The login page should be rendered incase of authentication failure and user should be notified so.
We can achieve this using a flash.Flash is used to send messages, either information or errors https://guides.rubyonrails.org/action_controller_overview.html#the-flash
We can inform the user using flash like so:
1 2 3 4 5 6 7 8
def create user = User.find_by(phone_number: session_params[:phone_number]) if user && user.authenticate(session_params[:password]) else flash.now[:error] = 'Invalid email/password combination' render 'new' end end
Let adjust our home page to give our user ability to sign in if they already have an account.
1 2 3
# truncated for brevity <p> Already have an account?<%= link_to "Sign In", login_path %><p> <p>New user? <%= link_to "Sign up now!", new_user_path %></p>
For us to create session for the user will user an helper which will be a module.create file
app/helpers/sessions_helper.rb and inside add the following.
1 2 3 4 5
module SessionsHelper def login(user) session[:user_id] = user.id end end
session[:user_id] = user.id creates temporary cookies with encrypted user id.This helps in creation of session for a particular user.session in this case in rails a method and has nothing to with our session control.To learn more about session.
For method login to be available to every controller in our application let us include the
sessionHelper module in our
ApplicationController like so.
1 2 3
class ApplicationController < ActionController::Base include SessionsHelper end
During login if the login was unsuccessful, we rendered login page and gave user an error message but incase of successful let us redirect them to home page displaying their name.
Showing the current user¶
The current user is the user in session i.e that who is logged in, we can identify this uniquely using their id and since we said
user_id in session is same as the
id of the user therefore we can retrieve him or from the database like so:
1 2 3 4 5 6
module SessionsHelper # truncated for brevity def current_user @current_user ||= User.find_by(id: session[:user_id]) end end
The above method check if @current_user already having a value if it doesn't it assign the value, which in this case will the user whose id is in session.If it already has it won't assign it.
Let us use condtional render to display current_user's first name.If they are in session otherwise give them display
Sign in and
Sign up link like so:
1 2 3 4 5 6 7
<p>Welcome to mpesa app<p> <% if current_user.present? %> You are logged in as <%= @current_user.first_name%> <%else%> <p> Already have an account?<%= link_to "Sign In", login_path %><p> <p>New user? <%= link_to "Sign up now!", new_user_path %></p> <%end%>
When user log our we delete the session.This details information about the current user stored in the browser. The let us setup our route like so:
# truncated for brevity delete '/logout', to: 'sessions#destroy'
Let us define the destroy method in our session controller like so:
1 2 3 4
def destroy logout redirect to root_path end
We have not yet define logout method which is called inside destroy method let us do so
1 2 3 4 5
# truncated for brevity def log_out session.delete(:user_id) @current_user = nil end