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.
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:
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:
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 the
ConversationsController. Where does
authorize come from? Well you can see from above that
ConversationsController inherits from
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
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
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
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:
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: