React Authentication (ft. JWT)

Winston Chen
6 min readFeb 12, 2021
Photo by Franck on Unsplash

Let’s talk about authentication.

When we were working with a pure Rails application, we were able to use cookies to send along with every request to make sure that our user was logged in. We send a cookie to make sure the user is who they say they are. We aren’t able to do this with a React frontend and Rails backend.

The frontend end communicates with the backend using HTTP requests, such as get requests, post requests, delete requests. How our frontend talks to our backend. One important thing about HTTP requests is that they are stateless. What this means is that the server does not remember anything about the client’s previous requests. So for example, we can make one request and get some sort of response, then the next response will have no idea that there was a previous request or that there will be more requests in the future. This is what makes these HTTP requests stateless. These are isolated requests and responses.

These stateless HTTP requests results in a problem for us in authenticating users. In other words, we don’t have a way to authenticate a user from one request to another unless we explicitly send information with our requests that would authenticate our user. The solution to this issue is JWT, or JSON Web Tokens.

You can learn more about JWT by checking out their website: https://jwt.io

In short, JWT is a way to authenticate our user if we have a frontend and a backend. So how does this work and what is the authentication flow? Here is a general sequence:

  1. User sees a form and inputs a username/password to request access
  2. Now a request to be made to the backend to validate that user be making sure that user exists in the database
  3. The backend will then send a token (JWT) to the frontend/client
  4. The client can store this token and present it with every subsequent request (to the backend)

So now we know the authentication flow. It’s important to note that the JWT is stored on the frontend. The frontend can then utilize the token to make sure that the correct user is accessing a particular site.

But what exactly is the JWT token? It’s just three parts that are separated by dots and each part represents a different thing. The first part is the header. The second part is the payload and the last part is the signature.

Here is the JSON Web Token structure outlined in a table:

JWT

The header typically consists of two parts, which are the algorithm and token type. The algorithm just encrypts our data and our token type is JWT. The signing algorithm being used as typically HMAC SHA256 or RSA.

The payload is the most important part of the JWT that you should understand because it holds the data or claims. Claims are statements about an entity and additional data. The three types of claims are registered, public, and private claims. It has some sort of user information, such as username/user ID.

Lastly, there is the signature. The signature is just essentially a hash of the header and payload. It’s hashed with a secret key that will encode and decode our payload.

So this token is something that is created by the backend. The backend sends it to the frontend and the frontend can store it in the browser. What is cool about this is that once it is stored, there is no concern that others can access that information because the token is encoded. Even if someone sees the token, they won’t know what is contained within it. It just looks like a bunch of gibberish to others, but the backend has they “key” to decrypt this token.

So why are JSON Web Tokens so important and why should we use them over Simple Web Tokens (SWT) or Security Assertion Markup Language Tokens (SAML)?

One reason is that JSON is less verbose than XML, which means that it is more compact than Security Assertion Markup Language Tokens. Thus, you should pick JWT because it is passed more fluidly in HTML and HTTP environments.

Additionally, JWT tokens can use a public/private key pair in the form of a X.509 certificate for signing. Thus, JSON signing is more simplistic.

Lastly, JWT is used at Internet scale which makes it easy to use on the client-side processing of JWT on many platforms, including mobile.

So now let’s talk code. The first thing is API. We should import API:

import api from '../services/api';

There is also a login function that we need to place in:

const login = (username, password) => {
return fetch(...)
method: 'POST',
headers: headers,
body: JSON.stringify({ username, password })
...
;

This function makes a POST fetch request to the backend, sending some headers, and then sending the username and password as the body.

This login function should be called using a handleSubmit in our login component:

handleSubmit = (e) => {
e.preventDefault();
api.auth.login(this.sate.fields.username, this.state.fields.password). then(res) ...
};

There should be a route in the backend that makes a POST, such as:

post '/auth', to: 'auth#create'

This will go to our auth controller, specifically with the create action:

AuthController < ApplicationController
def create
user = User.find_by{username: params[:username]}
if user && user.authenticate{params[:password])
my_token = issue_token(user)
render json:
...
...
end

In the create action above, it is first finding a user (the one that is typed in on the login page). Then we are checking if that user exists and if we can authenticate the user.

It’s important to note that the authenticate method above comes from the bcrypt gem. Thus, make sure that the gem is installed in the Gemfile.

So to summarize, when we get the user information (username and password) from the frontend, we are checking if the user is valid in the database and if they typed in the correct password to match the username. If all of that is true, then that user is issued a token. The issue_token is a method that is created and defined in the ApplicationController. Additionally, this is where the JWT gem is implemented and can also be found in the Gemfile.

def issue_token(user)
JWT.encode({user_id: user.id}, 'secret', 'HS256'}
end

From the above method, we are getting returned a token which can be found in the auth_controller. If you take a look at the token, then you can see that it is divided into 3 parts — divided by 3 dots — as I mentioned earlier in the blog. This token is what is sent to the frontend in order for the frontend to later be able to authenticate the user as represented here:

render json: (id: user.id, username: user.username, token: my_token)

If we take a look at the authentication flow again, we can see that we’ve covered the first 3 steps:

  1. User sees a form and inputs a username/password to request access
  2. Now a request to be made to the backend to validate that user be making sure that user exists in the database
  3. The backend will then send a token (JWT) to the frontend/client
  4. The client can store this token and present it with every subsequent request (to the backend)

But what about the last step, how is the token stored? This token is stored in the localStorage:

localStorage.setItem('token', user.token);

We can take a look at localStorage by typing in localStorage in the console. Items are added to localStorage by .setItem, which is shown above. We can later get that item using the below:

localStorage.getItem('token')

Any time a user visits our page and doesn’t clear the localStorage, then they’ll be able to access this token that we’re going to set in the storage. So this is how the token works and how we can sign users in.

But what about logging out? Logging out is simple. All you would have to do is remove the token or clear the localStorage, like so:

localStorage.removeItem('token');*or clear entire localStorage

Once we remove the token from storage, then the user will be logged out. Now, we can covered the entirety of the log in and log out flow that is created using JWT. You now have enough knowledge to embark on your journey in adding authentication to your React project!

--

--

Winston Chen

Full-Stack Web Developer, specializing in React and Ruby on Rails.