Skip to main content Link Search Menu Expand Document (external link)

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

  1. How to install devise
    1. Devise Helpers

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 add config.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/urlHttp VerbpathController#action
new_user_session _pathGET/users/sign_in(.:format)devise/sessions#new
user_session_pathPOST/users/sign_in(.:format)devise/sessions#create
destroy_user_session_pathDELETE/users/sign_out(.:format)devise/sessions#destroy
new_user_password_pathGET/users/password/new(.:format)devise/passwords#new
edit_user_password_pathGET/users/password/edit(.:format)devise/passwords#edit
user_password_pathPATCH/users/password(.:format)devise/passwords#update
 PUT/users/password(.:format)devise/passwords#update
 POST/users/password(.:format)devise/passwords#create
cancel_user_registration_pathGET/users/cancel(.:format)devise/registrations#cancel
new_user_registration_pathGET/users/sign_up(.:format)devise/registrations#new
edit_user_registration_pathGET/users/edit(.:format)devise/registrations#edit
user_registration_pathPATCH/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 for get requests, but we can change the behaviour of this by adding data: { turbo_method: :delete } now your http verb request will be delete. 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.


Back to top

Copyright © 2020-2022 Secure Smarter Service, Inc. This site is powered by KodaCamp.