Devise
This is one of the very popular gems that we use in Ruby on Rails. It handles the authentication for us with minimal work.
In this lesson, we will install devise in our application and learn how it works.
Table of contents
How to install devise
Reminder:
Make sure that your containers are up and running.
In your Gemfile, add gem devise
.
gem 'devise'
Then run bundle install
.
$~/KodaCamp> docker-compose exec app bash
root@0122:/usr/src/app# bundle install
...
Fetching devise 4.8.1
Installing devise 4.8.1
Bundle complete! 14 Gemfile dependencies, 66 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
Next we also need to run rails generate devise:install
.
root@0122:/usr/src/app# rails generate devise:install
create config/initializers/devise.rb
create config/locales/devise.en.yml
===============================================================================
Depending on your application's configuration some manual setup may be required:
1. Ensure you have defined default url options in your environments files. Here
is an example of default_url_options appropriate for a development environment
in config/environments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
In production, :host should be set to the actual host of your application.
* Required for all applications. *
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root to: "home#index"
* Not required for API-only Applications *
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
* Not required for API-only Applications *
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
* Not required *
===============================================================================
As you can see, this command generates two files which are the config/initializers/devise.rb and config/locales/devise.en.yml.
The config/initializers/devise.rb is the initializer file for Devise. Whenever you run your Ruby on Rails application, it loads all the initializer files. You can configure specific settings for different parts of your application from these initializer files.
If you open config/initializers/devise.rb
. It contains a lot of configurations. You can modify it according to your need.
Reminder :
Since we are using Rails 7 with
devise 4.8.1
, we need to addconfig.navigational_formats = ['*/*', :html, :turbo_stream]
in our initializer or else we will receive an error.NoMethodError in Devise... undefined method `user_url'
Devise.setup do |config|
# ...
+ config.navigational_formats = ['*/*', :html, :turbo_stream]
# ...
end
The config/locales/devise.en.yml file is used if you want your application to have multiple languages. We will discuss translations on another topic.
Devise recommends to add the notice
and alert
tags inside the body, right above <%= yield %>
.
3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p>
Since we already did it, we can skip this part. If you want to revisit the topic you can review it at the CRUD flash messages tutorial
Next, run this command rails g devise user
root@0122:/usr/src/app# rails g devise user
invoke active_record
create db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb
create app/models/user.rb
insert app/models/user.rb
route devise_for :users
This command generates several files and inserts some line of codes in our application.
# config/routes.rb
Rails.application.routes.draw do
+ devise_for :users # Devise inserted this line
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
root 'welcome#index'
resources :posts
end
Path/url | Http Verb | path | Controller#action |
---|---|---|---|
new_user_session _path | GET | /users/sign_in(.:format) | devise/sessions#new |
user_session_path | POST | /users/sign_in(.:format) | devise/sessions#create |
destroy_user_session_path | DELETE | /users/sign_out(.:format) | devise/sessions#destroy |
new_user_password_path | GET | /users/password/new(.:format) | devise/passwords#new |
edit_user_password_path | GET | /users/password/edit(.:format) | devise/passwords#edit |
user_password_path | PATCH | /users/password(.:format) | devise/passwords#update |
PUT | /users/password(.:format) | devise/passwords#update | |
POST | /users/password(.:format) | devise/passwords#create | |
cancel_user_registration_path | GET | /users/cancel(.:format) | devise/registrations#cancel |
new_user_registration_path | GET | /users/sign_up(.:format) | devise/registrations#new |
edit_user_registration_path | GET | /users/edit(.:format) | devise/registrations#edit |
user_registration_path | PATCH | /users(.:format) | devise/registrations#update |
PUT | /users(.:format) | devise/registrations#update | |
DELETE | /users(.:format) | devise/registrations#destroy | |
POST | /users(.:format) | devise/registrations#create |
These routes are the default modules of devise.
# app/models/user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
Devise composed of ten modules to configure your authentication. You can find these modules in your user model.
These are the default modules you can start to use as you install devise.
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable
- Database Authenticable —> Hash stores the password in your database. Authentication is done by a POST request. Necessary to save user/hashed password in the DB.
- Omniauthable —> Adds support for Omniauth providers, like twitter, facebook, google, github, etc.
- Confirmable —> Sends emails with confirmation instructions and verifies whether an account is already confirmed during sign in.
- Recoverable —> Resets the user password and sends reset instructions.
- Registerable —> Creates a registration process, users can now edit and delete their account. Disable for beta testing/invitation-only sites
- Rememberable —> Manages the generating and clearing a token for remembering the user from a saved cookie.
- Trackable —> Tracks sign in count, timestamps and IP address.
- Timeoutable —> Logs a user out after a certain amount of time.
- Validatable —> Uses built-in Devise validations for email address and password (length, characters, etc). Change validations in config/initializers/devise.rb
- Lockable —> Locks an account after a specified number of failed sign-in attempts. Can unlock via email or after a specified time period.
# db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
Reminder:
You can uncomment those lines or create a new migration file if you wish to use those modules.
Devise also generate a new migration file for our user
model so don’t forget to run rails db:migrate
root@0122:/usr/src/app# rails db:migrate
== xxxxxxxxxxxxxx DeviseCreateUsers: migrating ================================
-- create_table(:users)
-> 0.0214s
-- add_index(:users, :email, {:unique=>true})
-> 0.0159s
-- add_index(:users, :reset_password_token, {:unique=>true})
-> 0.0149s
== xxxxxxxxxxxxxx DeviseCreateUsers: migrated (0.0525s) =======================
Now, we are ready to run our application with rails s -b 0.0.0.0 -p 3000
. In your browser, go to http://localhost:3000/users/sign_up
. Let’s register a new user. Input your email and password after that click the sign up
button.
After you registered, you will be redirected to the root_path.
Remember that we added a <p class="notice"><%= notice %></p>
tag. After we registered successfully, it displays a successful message from devise
Devise Helpers
Devise provides us some helper methods that can help us to know that our users are already logged in.
user_signed_in?
This method returns true
when our user is signed in or and false
if not.
current_user
This method returns the authenticated user.
You can read the Devise Helpers for more information.
Let’s add the sign in
and sign out
links in app/views/welcome/index.html.erb
<h1>Hello World!</h1>
<p>Welcome to my page!</p>
+
+ <% if user_signed_in? %>
+ <h2> Hello <%= current_user.email %> </h2>
+ <%= link_to 'Sign out', destroy_user_session_path, data: { 'turbo-method': :delete } %>
+ <% else %>
+ <%= link_to 'Sign in', new_user_session_path %>
+ <% end %>
link_to
is use mostly forget
requests, but we can change the behaviour of this by addingdata: { turbo_method: :delete }
now your http verb request will bedelete
. You can add another parameter to add some confirmation.turbo_confirm: 'Are you sure you want to logout'
Now we already have authentication feature working in our Ruby on Rails application.
You can check Devise for more information.