The Tea of JWT — Ruby Implementation

If you’re looking into JWT auth for authorization / authentication on a project, and you’re unfamiliar with JWT, or inexperienced with authorization and authentication in general — you might be saying to yourself something along the lines of JWTF — and it probably doesn’t stand for “JSON Web Tokens, Fun!”

During the fourth phase of flatiron — and by during I mean I’m actually writing this in the midst of a project — you’ll be given the opportunity to implement some form of user authentication and authorization into a project using JSON web tokens. Following along with a walk through or example is easy enough, however actually understanding the implementation is another ball game in itself. For the sake of organizing my thoughts, I decided to write this blog to provide some extra insight in what is proving to be a very challenging portion of the project, and hopefully organize some of your thoughts as well.

Here’s the rundown of my implementation:

— An app with a Ruby on Rails backend with a React front end.

— A user model, in which a user will need to sign in to interact with the site.

— A user controller in which a user can at least be created.

— An auth controller, which will be needed for the authentication portion.

— An application controller, which should have been created already.

— Your routes file in the config. These will serve as the connection from the back end to the front end, and provide the endpoints needed to wire it all up.

Before we begin, let me add the disclaimer that JWT can be utilized in a variety of languages. For a full list, be sure to view the JWT documentation. Also, this is one of many ways to authenticate and authorize on your site. For production purposes, your methods may vary, and even with a JWT path, you’re methods may vary from mine greatly.

To start off, this blog will assume you already have an app in the works using the same framework, at least a user model / controller, and the ability to add an auth controller if you don’t already have one. You will also need the following gems installed to experience the fun of authorization and authentication with JWT:

— Bcrypt

— Jwt

— Active_model_serializers

Once you’ve got these gems added to your bundle and have run bundle install you can begin to fill out the methods needed on your controllers and make any necessary changes to your models.

We’ll start first with making sure bcrypt will work for the password encryption:

— Add “has_secure_password” to your user model, in addition to any validations you want.

Application Controller

Now for the meat and potatoes, lets add some methods to the application controller that will be passed on to the various controllers. (Remember, the other controllers will inherit from application controller) Here’s an example of what this might look like:

To explain some of the unfamiliar code you’re seeing in here, we’re getting some methods from the JWT gem specifically. Some of these method names can be reworded to better fit your vocabulary or use case, but the general purpose of this code will be the same. Just know that you will call on some of these methods elsewhere in your controllers, and if they’re naming is not uniform, you’ll run into all sorts of headaches.

First off — the “secret” and algorithm are better off hidden from plain site, one way to do this during development is with a .env for extra security. To do this, run gem ‘dotenv-rails’ and create a .env in the root directory with “JWT_SECRET=’yoursecretkey’”.

— Issue token: takes in a JWT payload, and uses your specified “secret” to encode the payload. Note- The secret is an arbitrary phrase that is used in the encoding process.

— auth_header: grab the header from the API call.

— decoded_token: decodes the token.

— current_user: uses the decoded token to identify a user.

— logged_in?: verifies if a user is logged in.

— authorized: limits a user’s ability to access certain actions without auth.

User Controller

Next step is to get the controllers set up for sign up. Starting with the User model:

— Build a create function to create a user when signing up. Notice in the picture below that there is a method call, “issue_token” that is being inherited from the application controller.

— Add a skip action to skip the “authorized” method inherited from the application controller. This will be used again in the auth controller, and potentially in other controllers if you don’t want to require authorization on specific actions.

— Time to get your routes drawn out. Note, there are two custom routes here that aren’t needed for sign up — but will be covered in just a moment when we go over login.

IN THE FRONT END:

This will likely be accessed via a Sign Up form, which will be wired up to make an API call to the create route, and might store the JWT token that is returned in in the JSON response.

Auth Controller

Here is a place where your methods may differ greatly from mine. For the backend, the login can be handled through the auth controller if you’re utilizing one. For my preference, since a log in is not creating a user, there is no need for this to be on the user controller. However, you can put this on the user controller instead if you’d like.

— Define the login method and add a skip before action call to skip auth being required to perform a login action, so that we don’t require a user to be logged in to log in. That would be silly! Note again, this is inheriting from application controller so we have access to the methods defined there for issuing tokens.

To provide some context, the login function will search for a user, and if found, will authenticate and encode a token to be issued. Afterwards, it will render json containing user attributes and a token.

— Get the route set up for login. As seen in the picture below (same as route pic above), you’ll create a custom route. Note — if you chose to login using the user controller instead of auth, be sure the route reflects this.

IN THE FRONT END:

This will likely be accessed via a login form, which will be wired up to make an API call to the login route and storing the JWT token that is returned in in the JSON response.

Staying Logged in — User Controller

At this point you have a backend that is built out and capable of signing up a new user (creation of a new user) and logging in. You may notice however that you will not stay logged in when the browser refreshes, or if you navigate to another page. To get around this, you’ll need to set up a method on either the user or the auth controller. For this project, I went with user. Although it makes perfect sense to have this on the auth controller instead if preferred.

— Add the “getuser” method. See the picture below, displaying a full user controller, with the new “getuser” method up top.

The purpose of this method is a little less clear until you’ve built out the front end, can log in or sign up, but find that you’re not staying logged in on a page reload. With “getuser” we are allowing the user controller to authorize the current user, and return the user information from the serializer. This could be information such as “user_id” and “username” for example. On the front end, this information would in turn be stored for browser access.

— Connect this custom method to a custom route:

Once the “getuser” method is established, you’ll need to set up a route that will hit that method and connect the user information from the backend to the front end.

And that’s it for the backend! As the backend comes to an end, the front-end fun begins. If you haven’t already done so, you’ll need to organize your sign up and sign in forms and build a sign out function in the front end.