There are simple rules I have learned to live by when writing code. The first one is Don't Repeat Yourself (DRY), the second is to always make sure I comment my regex, ( You should comment all your code, but mostly your regex ) and finally, always find out if there is a tool that can help you build what you're building faster and better.
Why Devise??
Now if you're anything like me, you learnt rails by reading Michael Hartl's book on Rails. The book is a gem (pun intended) and I would recommend it to anyone who wants to learn rails and the TDD methodology. In the book, when building the Users model, Michael uses the has_secure_password
for user authentication. Now this works pretty well for that example, and it is what I used because it is what I knew. That was until I went on r/rails and found out about Devise.
Well Devise is this wonderful gem that is used for UserAuthentication. It handles a lot more than I can explain on here, but basically, instead of having to build the specific controller methods for authentication, managing failed log-in attempts, locking accounts and account recovery, you just install devise on your app and you will have all this available to you.
How to Devise
First off, the docs are always an invaluable resource so I will ask you to refer to that before copy pasting my code. Devise Repo.
So let's do basic User authentication.
Step 1 - The Model
Add the Devise gem to your gemfile
gem 'Devise'
then run bundle install.-
Run the generator
rails generate devise:install
. At this point, a number of instructions will appear in the console. Among these instructions, you'll need to set up the default URL options for the Devise mailer in each environment. For example in your development environment, (config/environments/development.rb
)
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
- Generate the devise model just how you would any other model `rails generate devise user` and then run the migration `rails db:migrate`
-At this point, the official rails documentation recommends restarting the rails app. This should also include stopping spring (`spring stop`), in order to avoid running into weird errors.
Worth noting is that your model will have some interesting code added to it. Specifically, the devise methods in your models will have some options used to configure its modules. It would look something like
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable :validatable
### Step 2 - Routes
- To configure your routes for devise, add the following line in you `config/routes.rb` file
devise_for :users
- However, if you need more specific routes, you can just create your routes noramlly and then wrap them in a `devise_scope` block in the router. For instance:
devise_scope :user do
post 'sign_in', to 'devise/sessions#create'
end
### Step 3 - Controllers.
- For the purposes of this example, we will just build the sessions controller for sign in and sign out.
- The controller using devise would not look that much different from your regular controllers. In this case it would look something like this.
class SessionsController < Devise::SessionsController
# sign in
def create
if @user.valid_password?(sign_in_params[:password])
sign_in "user", @user
render json: {
messages: "Signed In Successfully",
is_success: true,
data: {user: @user}
}, status: :ok
else
render json: {
messages: "Signed In Failed - Unauthorized",
is_success: false,
data: {}
}, status: :unauthorized
end
end
private
def sign_in_params
params.require(:sign_in).permit :email, :password
end
def load_user
@user = User.find_for_database_authentication(email: sign_in_params[:email])
if @user
return @user
else
render json: {
messages: "Cannot get User",
is_success: false,
data: {}
}, status: :failure
end
end
end
Now that we have our model, routes and controller set up, we can boot up our server with ```rails
s
``` and test these endpoints with Postman. (or cURL for the CLI diehards)
The JSON object for the request in Postman would look like this
{
"user":{
"email": "email@example.com",
"password" : "foobar123"
}
}
( *Please note that this assumes you have a user in your database with the email and password shown above* )
when you send the POST request to *localhost:3000/sign_in* you should get that sweet 200-OK response.
Well, that's basically it. You have devise running on your app providing functionality for User Authentication.
All that is left now is to restrict access to the other models only to those users who are signed in. Remember when I called Devise a swiss army knife? Well, it comes bundled with a lot of helper methods, one of which is `:authenticate_user!`
So for any model that you need a user to be authorized before access, just add:
`before_action :authenticate_user!` on it's controller.
This can also be augmented to specific variations like
```before_action
:authenticate_user!, only: [:create]
This restricts the create action to only authorized users who are signed in.
Hopefully, this covers what you need to know to get started with the devise gem. It is not enough though, so again, read the docs.
As a side note, also check out devise_token_auth
here