React & Rails Auth From Scratch using JWT Part III

Kevin Glasgow
Level Up Coding
Published in
11 min readMar 6, 2021

--

Prerequisites:

React installed

Rails installed

Welcome back everyone, this is the third and final part of the React and Rails authentication build. As you remember from part II, we added to our sign-up feature by also creating a sign-in feature as well that authenticates our users. If you have not followed along during the first or second part, you can find a copy of the repo we will be starting from today here.

If you have just copied the repo down, you will need to run:

On the backend:

On the frontend:

If you have been following along in parts I & II, you will only need to start your servers by running the last line of code for each.

Now that we have both of our servers up and running, we should see our sign-in form and our sign-up form. Both should still be in working order and land us on our landing page. For those who have just cloned the repo, you will need to first create a user to make use of your sign-in feature since your database will be empty.

There is one little issue that we will need to fix with our app today, that being on page refresh, our user will present with our sign-in or sign-up forms. This means that we are losing the state of our user on refresh, and that is something we will want to fix. We will be authorizing our users which will help us keep the state of our user throughout the app. This is achieved by utilizing the token we had previously placed into local storage through our sign-up function.

To get things started we will need to head over to our backend and open up our application controller. This is where we will be creating our method called is_authorized that will, you guessed it, check to make sure that our user is in fact authorized. In this case if our user is authorized, they will have access to their user information. If not, then they will be presented with an error asking them to sign-in so that user information can be accessed. Your authorize method should look as such:

From the code you can see that our user will get an error unless they are signed in. We called on the method, is_signed_in, and now need to build that out. To do so we will simply need to check if a user is currently using our application by setting our current user to a Boolean value. Offering us a truthy or “falsey” value for the current state of is_signed_in. This in turn means we will also need to build out our current_user method which will check the legitimacy of the token provided to our users. We can extract our users token from our backend request’s authorization header. Your two new methods should look as follows:

Now when our current user runs, we will get back a string containing bearer and the token. We will first need to create an if statement taking in the argument of the auth_header. We will only need the actual token from our authorization header, to do this we will simply turn that string into an array and cut off everything but the user’s token. Your if statement, within your current user method, should look like such:

Now that we have access to our user’s token, we will want to go ahead and decode it. This is to ensure that the user is who they claim to be through their original encoded token, that we encoded earlier through our login method, is in fact the same. In our case that would refer to our users id, since that is what we chose to encode.

This is somewhat of a delicate process due to the fact if the token does not exist, rails will give us a nice big 500 error when it tries to decode it. This will stop our app in its tracks and that is something we do not want to experience let alone our users. To avoid this issue, we will by using a begin and rescue, this will let us try to decode our received token. Should we encounter an error, such as the 500 I spoke of previously, the rescue will allow us to avoid such errors that would otherwise lockdown our app. Your if statement should be updated as such.

Now that we have our safety net ready, lets start building it out and get our user’s token. This will look similar to when we encoded our token in our login method, with a few differences. To do this we will call on JWT to decode our original token, which contains our user’s id as well as our secret since that is what we encoded from our user within our login method. It is vital that you encode and decode the same information, otherwise you will not receive what has been encoded.

We will be returned an array once JWT has decoded our token, and from our decoded token we will want to pull out their id. We can simple do this by calling on the index of the object we encoded, our user’s id, then pulling it from its key we will be left with the users id. We will also set this to an instance variable of user id since we will be left with a users numerical id, should everything go smoothly.

In the event that everything does not go smoothly, our rescue will save us from the impending error we would about to receive. We will use our rescue to stop JWT’s decode error from running and in this case will return nil, or nothing. Your updated begin and rescue should look as follows.

Now that we should have our user’s id, we still need to get our user which we will do based off their id. To do so we will create another if statement following the last one, that we just finished. If we have a user’s id, we will then find that user and return them, or return nil. In both instances of us returning nil within this method, would cause our custom error to render from within our is authorized method. Our first instance would stop our JWT decode error and in this instance, it would stop the active record error should that user id not exist within the database. Your completed current user method should look as follows.

Our current user method definitely has a lot going on, but is essentially doing what our login method did, but in reverse and then returning said user. Now that we have the ability to tell if a user is authorized or not, let’s make sure only authorized user can access our app. To do this we will create a before action at the top. Our before action will take in our method of is authorized and will now lockdown our app completely. Your before action should look as follows.

If you would like to see an example of this try going to one of your routes such as users. You should be greeted with the custom error message that you created

This poses one problem and that is how can we authorize a user before they even sign-up? Do not worry this is not a trick question, you cannot! But what we can do is create a skip before action, that we will place in our users controller. Before we get too ahead of ourselves, your completed application controller should look as such.

I have a skip before action due to a rack-cors issue with rails, you may or may not need this in your code depending on how part I of this walkthrough went for you.

Let us head over to our user’s controller now, and right at the top we will create a skip before action for is authorized. The small issue with this is that we have now skipped our is authorized method making it practically useless now unless we give it some parameters. Essentially what to allow and what not to allow someone to do if they are not authorized. The two methods we will want a user to be able to access before they are authorized is to either sign-up, which would be our create method, and to sign-in with out create method. When still in development I personally will include the index action of all my controllers but would not recommend for deployed apps. This will allow me to see what is held within my rails database, localhost:3000/your route, after users need authorization. Your skip before action should look as such.

The last piece of this puzzle is that once our user has finally been authorized, we will want to grant them access to their data, exactly what you would see on your profile. To do this we will simply create a new method under out skip before action called user_profile. If you have created your controller and tables by using rails g scaffold, our users profile method we are about to build will simply be a show method. In our user profile method we will simple want to show or render our user. Your updated users controller should look as follows.

Note that we are grabbing our user from the return value of current user within our application controller

Lastly, we will need to set up our route for our user’s profile and will add a get request for our new method. This will differ from that of index in that it will only show that authorized user’s information instead of all user’s information. Also, even after setting up your new route, you will still be locked out from accessing it. Showing the strengths of what your auth can do for you. Your updated routes.rb should look as follows.

Now it is time to slide over to our frontend where we will actually do what was promised, keeping our user present in state even upon refresh. This is all thanks to our token, and to get things started we will want to head over to our App.js. Currently every time that we refresh the page, the components on the page are being remounted and we need to alter how that is done.

To achieve this, we will use a component did mount with an if statement so that when the page does refresh, our user will already have authorization to access our app. We will first need our token from local storage, which we will set to the variable of token. If our app has a token, we will make a fetch to our new route that we have made, profile. It will be similar to its route in that it is a get request, but we will be looking to send the authorization header with our token. We will need to format our response as it should be received, which is as a string containing bearer and the token. Your completed fetch should look as follows.

Be sure to user string interpolation to include our variable of token

The response from our fetch call will either return us our authorized user or our error. This means we will need to do some conditional rendering in the form of an if statement to handle each of the possible outcomes. To do this we will check if our result contains something that a user object would contain. As you have previously seen, I prefer to use a user’s id whenever dealing with them so that is what we will check for. If an id is present in our result, we will then set the state of user to our result because the result is the user object. Your completed component did mount should look as follows.

Now once your user signs in and the page refreshes, your user will remain authorized to be in the app. It should be noted that your component did mount function will need to be passed down to the other pages you will be adding to your app. As you cannot pass down you would create a function called validation which contains everything currently in our component did mount. This function can be passed down to your other pages while still being called on your current page as such.

Congratulations you did it! If you have followed these steps correctly you should now be able to create a user, have them sign-in, and to keep that user authorized to use our app once signed in. If you have you have cloned down the repo just for this part of the walkthrough, you will first need to create a user to sign-in. You can also test out your error message by entering nonsense into the sign-up fields, as well as trying to visit any of the routes we have made private on the backend. Any user that has a token will now be authorized to use our app once they have signed in.

If you would like to look at the completed repo, you can find it here.

Thank you for following along, I hope this three-part series on authentication has helped you as much as it did me! Please keep an eye out for future blogs, tips, and walkthroughs!

--

--

Full Stack Software Engineer | Passion for Frontend and Design | Dirt Rider & Snow Glider | www.linkedin.com/in/kevin-glasgow/