JSON Web Token Integration in Rails

In most applications you will likely have user accounts. And if you do, you certainly want to limit what a particular user has access to. JSON Web Tokens is a popular method for providing user authorization across resources inside your application on the client-side.

Image for post
Image for post

When you send a normal fetch request to your backend, you typically send the method of the request (GET, POST, etc) along with a header object. Something like:

Image for post
Image for post

This works great, but there isn’t enough information sent in this request to authorize the user once the request hits the backend. The additional piece of information we need is a JSON Web Token. Once a user is logged into the application, all subsequent requests will be sent along with the unique token, which provides the user access to any routes and resources that are permitted with that token.

The token should be sent as another key/value pair of the headers object. The key will be “Authorization” and the value will be “Bearer token", such as:

Image for post
Image for post

This request will hit the appropriate controller in your Rails backend. In this controller you will have several methods which represent actions for your routes. A common way to authorize access to these methods/actions is to use the before_action macro that Rails provides at the top of your controller class.

Above, we are executing the require_login method before running any of the action in theConversationsController. Where does require_login and authorize come from? Well you can see from above that ConversationsController inherits from ApplicationController. So ApplicationController is where those two methods live. And since ApplicationController inherits from ActionController, any method in ApplicationController will be accessible inside all of our other controllers.

Ultimately, our request is going to the index action inside ConversationsController:

Image for post
Image for post

Below is what ApplicationController looks like. We will go over all the code here to see what is happening as our request unfolds.

So, recall the require_login and authorize methods are executed first as we invoke it with the before_action macro. The require_login method will render a JSON error object if the user is NOT logged in. The authorize method will render a JSON error object if the logged in user is NOT the user that has the user ID passed in the URL. This means the index action will not actually execute if any of these error objects exist, so authorization is working. But how?

How do we know if a user is even logged in? We can see ApplicationController has a logged_in? method which returns the double bang operator for the current_user method. So a user is considered logged in if the logged_in? method returns true, which means the current_user method would have to return an actual object (user) in order for the double bang operator to yield true.

In order for the current_user method to return an object (user), the decode_token method has to return true. The decode_token method is where the magic actually happens. The token that we sent in our fetch request with “Authorization” headers is decoded here with “JWT.decode” with our token passed in. From the decoded result, we parse out the user id inside the current_user method. We then use the find_by Active Record method to locate a user with that user id. From there, we are able to not only check if a user is logged in, but ensure that a user is authorized to view that content.

What’s great about the before_action Rails macro is that we can restrict any of our actions/controllers with a single line of code. I can utilize the require_login method in my UsersController just as easily:

Image for post
Image for post

A question you may have is where does token actually come from on the frontend when sending the request? The option I used is localStorage, which may or may not be the best way to go. You can store the token in localStorage with the following line in your login route success response: localStorage.setItem(‘token’, jwt)

That’s it!

https://www.dougschallmoser.com/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store