SETTING UP AUTHENTICATION IN RAILS API USING DEVISE AND JWT PART 1.

Rose Sumba
4 min readAug 11, 2021
Markus Spiske from Unsplash

I was once building a full-stack project using Ruby on Rails as my backend and React as my frontend. However, setting up JWT and devise for authentication was very challenging for me. I used to jump from one tutorial to another without solid information on how I would tackle it. Luckily, with some guidance from my friends and of course spending tons of hours researching, I successfully set it up and would like to share what I learned.

Since it would probably be a long article I decided to divide it into two parts so that it may be easier for you to munch on. So let’s get started!!

In this article, we are going to do the basic configuration to set ourselves up and running with jwt and devise.

What is JWT though?

A JSON web token, or JWT in simple terms, is a standardized, optionally validated, and/or encrypted container format that is used to securely transfer information between two parties i.e a client and a server.

In authentication, when the user successfully logs in using their credentials, a JSON Web Token will be returned. Whenever the user wants to access a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema. You can go through JWT’s official documentation to learn more.

Let’s start off by initializing our rails API. Run:

rails new name_of_your_app --api --database=postgresql

Once everything has been installed move into the rails project folder and in your ./Gemfileadd the following gems:

gem 'jwt'
gem 'devise'
uncomment
gem 'rack-cors'

Then run bundle install

We now want to install devise and you can do that by running this command:

rails generate devise:install

And you will see the following instructions on your console. Note that the first one is the most relevant since this is an API-only application.

===============================================================================Some setup you must do manually if you haven't yet:
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.
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:root to: "home#index"
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>
4. You can copy Devise views (for customization) to your app by running:rails g devise:views===============================================================================

SETTING UP THE USER MODEL

One of the coolest features about devise is that it builds the user model for you just by running the following command:

$ rails g devise User

In the db/migrationfolder you will see the devise_create_user migration file that was generated. Run

rails db:create then rails db:migrate .

CONFIGURING CORS AND WARDEN

So now let’s setup cors by adding the following to the ./config/initializers/cors.rb file:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*', headers: :any, methods: [:get, :post, :patch, :put]
end
end
  • This will help to avoid CORS issues when API is called from the frontend app.
  • Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. Read more here https://github.com/cyu/rack-cors.

Next, we need to configure warden in our ./config/initializers/devise.rb which will be our devise strategy to use JWT.

Around line #280 add the following code config.warden do |manager|# manager.intercept_401 = falsemanager.strategies.add :jwt, Devise::Strategies::JWTmanager.default_strategies(scope: :user).unshift :jwtend

And at the very end of the file, add the following code. Note that it should be outside the last end tag.

module Devisemodule Strategiesclass JWT < Basedef valid?request.headers['Authorization'].present?enddef authenticate!token = request.headers.fetch('Authorization', '').split(' ').lastpayload = JsonWebToken.decode(token)success! User.find(payload['sub'])rescue ::JWT::ExpiredSignaturefail! 'Auth token has expired'rescue ::JWT::DecodeErrorfail! 'Auth token is invalid'endendendend

Lastly, let’s create new a file in ./app/models by naming it json_web_token.rb and place the following code:

class JsonWebTokendef self.encode(payload)JWT.encode payload, Rails.application.secret_key_baseenddef self.decode(token)JWT.decode(token, Rails.application.secret_key_base).firstendend

That’s it!!

This is all the configuration we need for now. In the next article, I will show you how to set up the API endpoints using namespaces and configuring our controllers. We will also add another model to associate with our user model and see the token working in action while testing with postman. See you there!!

If you liked the article please show some love by applauding it. Also, if you have any questions let me know in the comment section.

PART 2

Cheers and happy coding. 🚀

--

--

Rose Sumba

Full Stack Developer(Ruby on Rails and React and Redux)