Simple JWT Authentication with Rails
By: Robert Mejia
Tags: Jwt, Rails Api, Authentication, Rails, API

Introduction

In this post, we will create a simple rails API server that is able to generate a JSON Web Token. JSON Web Token, also known as JWT (pronounced “jot”) is an open standard (RFC 7519) that allows for claims or information to be transmitted in a compact and self-contained way. These pieces of information are digitally signed or integrity protected with a message authentication code (MAC) or signature. JWT can be signed using a secret key using the HMAC algorithm. An HMAC algorithm is a hash-based message authentication code. We can also use a public/private key pair using RSA or ECDSA.

JWT has a few benefits. For starters, they are language agnostic. The tokens are self-contained. It contains the information needed such as the payload, information about itself, and the signature. The token structure consists of a header, payload, and signature each separated with a dot. Therefore, a token typically looks like the following “xxxxxx.yyyyy.zzzzz”.

When should we use JWTs?

The most common use case for using JWT is for authorization. In our rails backend, we would like our users to be authenticated in order to access our resources! When a user is authenticated we can have our backend generate a token with any relevant data we may need and send it back to our user. Our users would then have to submit their token with every request to our resources that may require it. Our backend will then decode the token sent by the user and make sure the user has access to the resource.

Lets code

To get started let us create a new rails project using the rails new command in our command-line interface. Next, we create our Postgres database. Make sure your Postgres server is running! Once that is finished, we will use rails generators to create a users resource with a username and password and no test framework. At this point, we should run the migration generated by rails. Finally, we will create a login controller by using the generator once again.

terminal commands

Now, we still should not run our rails server. We are missing a few more steps. First, you should now open the gemfile . Located the line comments out the gem for bcrypt and rack-cors uncomment them. Next, we want to add the jwt and dotenv-rails gems. Remember to edit your cors.rb file located in config/initializers an example of what this file should look like follows. At this point run bundle install to update our gems.

cors.rb example

gemfile

The jwt gem is a ruby implementation of the RFC7519 open standard. It is important to note that currently the gem only supports NONE , HMAC , RSASSA , and ECDSA for cryptographic signing. Documentation for the gem is located at Github.

The dotenv gem allows us to load the environment variables from a ‘.env’ in development. Please create a file named .env at this point at the root of your project. Once created go into your .gitignore file and add .env in it. In this file you will add a key-value pair with the secret you are going to use to create the tokens. You can run your rails server but we still cannot generate tokens. Let us do that next.

.env example

To get us started with generating a token we should add some helper methods to our application controller. Open application_controller.rb and add the following method definitions.

These helper methods are all we need to generate and use our authorization tokens. The token method takes in a user id and uses it to create a payload. It then returns makes a call to the JWT encode class method which returns to us a signed token with the payload. We return this token as well.

The hmac_secret method just gets and returns our secret from our environment. The client_has_valid_token? method calls the current_user_id value and returns a forced boolean. The current_user_id method tries to pull out the user id from the token by using the JWT decode class method. If it successfully does this it returns the user’s id. If not, an exception is thrown and nil is returned. Finally, the require_login method will be used as a before action inside of our controllers. It checks if the client has a valid token rendering an error to the client if it does not and does not allow access to the resource.

Note you must specify the algorithm again in the decode method to ensure an attacker does not bypass the algorithm verification step. The decode method returns to us an array. The first element in the array is the payload the second is the header.

Let us see how you would use the token method inside login_controller.rb to send a user back a token if they are successfully authenticated.

First, the create method tries to find a user with the username sent in the params. Next, if we find the user we try to authenticate the user with the password sent in the params. If we are able to, we render a JSON object that contains the token and the user's id. Note, it may feel redundant to add the user id since its part of the payload, but it is there for convenience. Feel free to add whatever works for you. An error is sent if authentication fails for any reason.