Authentication. You don’t always want your users to have faceless sessions that open your application without leaving any trace.
Of course, there are some applications in which you don’t need accounts for your users, but in the vast majority of the cases, you will need to manage some users and passwords.
If you are a beginner with ruby on rails you may already be thinking how to do this. Create a user model, add some fields to your user model, including an encrypted password field.
Save your user’s session in some way so they can remain online even after switching application windows, save some cookies so they can remain online even after closing the browser. You know, those things are pretty repetitive if you create new applications often, and if something is repetitive, you can make an algorithm that does it for you.
The special thing about the internet though is that most of what you think about creating was already created by someone else. In the case of authentication for Ruby on Rails, there’s a gem for that.
Devise is a very complete gem that does all the authentication work for you, or most of it if you are thinking about a very specific feature you would want to implement.
When I first used it, I just wanted to make a basic login for my users and add some extra features to the registration process. It took me a couple of hours to read the documentation and add what I wanted, and I wish I could have found a more basic tutorial to guide myself with.
I want to do my past self a favor and make a short tutorial on how to create a Rails application, add some extra fields to the user model, and modify the default views of the gem.
Let's begin. Create a project first if you haven't already.
And immediately after, add devise to the gemfile.
gem 'devise'
Then run bundle install on your terminal.
$ bundle install
Wait for everything to install and when it's done, run this other command on your terminal:
$ rails generate devise:install
You can see we got some instructions with this one.
The first one is referring to the mailer settings. For a development environment, you need to specify your default URL. This won't give you trouble if you are not going to send mail to your users, but let's copy and paste the line on the development.rb file just in case we need to send mail to our users.
The second point asks us to define a root_url to something. I will leave this for later, you don't really need anything on the root path of your application for devise to work. This is just the gem reminding you to make a home page.
The third point asks us to ensure we have flash messages on our application.html.erb file. Why? Well, this will let the users know if they are doing something wrong.
Default messages are already included on the devise gem so you don't have to write them. Just copy and paste what devise shows you on the terminal wherever you want it to be visible.
The last point is, in my opinion, the most important one. It's telling us to do generate the views of the devise for customization. We will run the command now and modify the files later.
Great! We can now generate our model. You can call your devise model whatever you want, I will call it "User". It's a generic name understandable by anyone. Let's run the command now.
$ rails generate devise User
And before we migrate our database, let's go check the migration file.
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[6.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
Here, you can uncomment the fields you want to use, for example, if you want to be able to confirm your users sending them an email, you can uncomment the four lines below # Confirmable. The devise gem will do the rest (plus some configuration of your own you will have to investigate, as it's not going to be covered in this tutorial).
Let's add a first and last name to our users, just to give you an example on how to do it. We will use the same model devise created to add any fields you wish but will need to do some modifications later. Let's create a new migration now.
$ rails generate migration add_name_to_users name:string surname:string
This will generate a new migration file which will add some columns to the user table. Let's go check the migration file.
class AddNameToUsers < ActiveRecord::Migration[6.0]
def change
add_column :users, :name, :string
add_column :users, :surname, :string
end
end
Remember, I didn't write any of this on the migration file, it was generated by rails. Everything seems to be good, so we can do the database migration. On your terminal, you can either do
$ rails db:migrate
Or you can also write:
$ rake db:migrate
Either will work. After doing the migration, let's go check the sign-up form on our app. We haven't done anything at the moment so we will need to access it manually by typing the route in. First of all, run your server
$ rails server
Then, go to any internet browser you have on your computer and go to this address: http://localhost:3000/users/sign_up . What you will see is your sign-up form, auto-generated by devise. It doesn't have any special style, but you can take care of that. What I'm concerned about is something else. We added a name and surname to our users and it's not here! Let's go change that. Go to the views folder. Remember we ran the command rails g devise:views? It will come in handy now. In our views folder, we have another folder called devise. Open it and search for the registrations folder. Here, you have the new and edit files. Let's open the new.html.erb file.
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
This is the signup form, yes. But it's lacking the two fields we wanted to our user. Let's add them, actually, let's add these two fields to both the new and edit files. The edit file, by the way, is for old users who want to change their information, like their email or password.
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :surname %><br />
<%= f.text_field :surname %>
</div>
I just immitated the stile set by the auto-generated sign-up form from devise. You can style it later with your own css. Let's see how it looks.
You should do the same with the edit.html.erb file while you are at it. For now, let's test our sign-up form. I haven't added any more functionality to this app, but we should be able to add something to our database with this form. Let's first run the console and check if we have something there.
You can see we get an empty array when we use the User.all command on the console. Let's go back to the sign-up form and... sign-up.
After clicking on the Sign-Up button, we are sent back to the root route. This is the intended behaviour. Let's go check the console again and see what we get when we search for all of the users. We should only get one user and it sould be mister foo bar.
Did you notice something? The name and surname fields on the user record were not saved. This is because we need to explicitly tell rails to accept these two fields in the form. Let's go do that now. On the controllers folder, open the application_controller.rb file and update it so it looks like this.
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :update_allowed_parameters, if: :devise_controller?
protected
def update_allowed_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:name, :surname, :email, :password)}
devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:name, :surname, :email, :password, :current_password)}
end
end
Let me go to the console and erase the first user from there. Now let's test this again, after the changes, we should be able to sign-up and have all of our user fields inserted in the database.
I'm using the same credentials as last time. Again, I already erased the first mister foo bar from the database so it shouldn't be a problem. Now let's click Sign Up and check the database on the console.
We got it now.
Congratulations, you now know how to install devise in your applications, add a model with devise, add some fields to those models and customize devise's views. You can now continue with the rest of your web application.
Thank you for reading!