Rails API + JWT Authentication
How to build simple Rails API authentication using Json Web Token (JWT)
What we need
- Ruby 2.5.1
- Rails 5.2.1
- Insomnia / Postman
REST API table
Okay now we’re ready to generate rails project, type this on your terminal
$ rails new rails-jwt --api
Add Json Web Token (jwt) and bcrypt gem
- JWT : Token encoder / decoder with expiration time
- bcrypt : Password encryption
# Use Json Web Token (JWT) for token based authentication
# Use ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.7'
and then install dependencies by typing this on your terminal
$ bundle install
# config/routes.rbRails.application.routes.draw do
resources :users, param: :_username
post '/auth/login', to: 'authentication#login'
get '/*a', to: 'application#not_found'
In the routes.rb, we defined routes for users using resources. resources syntax help us for generating REST API pattern path for user using _username as parameter. So it will looks like our REST API table above.
Create JsonWebToken class
SECRET_KEY is the key for encoding and decoding token. In the code above, we assign secret key that generated by default by rails application into SECRET_KEY variable. SECRET_KEY must be secret and not to be shared. Everytime we’re doing some encoding and decoding using JWT, we need to specify the SECRET_KEY. By grouping and encapsulating JWT encoding and decoding mechanism in this class, we will reduce couple of code that have responsibility for doing encoding and decoding job, because we don’t need to specify SECRET_KEY everytime. Decode and encode function above defined as static function because it will give a flexibility for doing encoding and decoding job without instantiate JsonWebToken object.
self.encode function has 2 parameters. first payload and second exp. payload is key-value object for holding data that want to be encoded. exp stand for expiration for holding expiration time token. if exp not specified it will give you default value in 24 hour or one day.
In self.decode function we decoded the token that given by user and get the first value then assign to decoded variable, the first value contain payload that we had already encoded before and second value contain information about algorithm that we use for encoding and decoding token.
Create authorize_request function
authorize_request function has responsibility for authorizing user request. first we need to get token in header with ‘Authorization’ as key. with this token now we can decode and get the payload value. in this application we define user_id in payload. You should not include the user credentials data into payload because it will cause security issue, you can include data that needed to authorizing user. When performing JsonWebToken.decode function, it will return JWT::DecodeError if there was an error like token was expired, token not valid, token missing etc. After we got user_id from payload then we will try to find user by id and assign it into current_user variable, If user not exist it will return ActiveRecord::RecordNotFound and it will render error message with http status unauthorized.
Create user model
$ rails g model user name:string username:string email:string password_digest:string
add user validation
Create user controller
$ rails g controller users
Add Create, Read, Update, Delete (CRUD ) functionality
Create authentication controller
$ rails g controller authentication
Implement login feature
In JWT there is no way to invalidate token, you can use one of this approach to implement logout feature :
1. Remove token from client, but token still valid, in my opinion you should use short time period token.
2. Add token into blacklist, when token added into blacklist token still valid until expiration time but you can deny this request from accessing resource.
Now you can test your application response with insomnia or postman