Spencer FengSpencer Feng

Authentication in React Apps with JWT: Part 7

Welcome to the seventh tutorial in our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. Users have been able to signup and login to the application and their login status can be displayed. But, if you refresh the web page when you have logged in, you will see that you are not logged in anymore. So, in this tutorial, we will persist a user’s login status after the web page is refreshed.

  • Create the route on the server side to authenticate a user by a JSON Web Token
  • Create our Redux actions to update the app state after a user is authenticated by a JSON Web Token
  • Dispatch the action

Create the route on the server side to authenticate a user by a JSON Web Token

The code below creates the route which authenticates a user via JWT and please add it to the ‘app.js’ file.

// create the route to handle user authentication via JWT
app.post('/authenticate', function(req, res, nex) {

    res.setHeader("Access-Control-Allow-Origin", "*");

    const token = req.body.token;

    jwt.verify(token, process.env.JWT_KEY, function(err, payload) {

        if (err) {
            return res.status(403).json({
                success: false,
                message: err.message
            });
        }

        const userId = payload.sub;

        User.findOne({
            '_id': userId
        }, function(err, user) {
            if (err) {
                return res.status(400).json({
                    success: false,
                    message: err.message
                });
            }

            if (user) {
                return res.json({
                    success: true,
                    message: 'The user was found',
                    user: {
                        _id: user._id,
                        firstName: user.firstName,
                        lastName: user.lastName,
                        email: user.email
                    }
                });
            } else {
                return res.status(404).json({
                    success: false,
                    message: 'The user could not be found'
                });
            }
        });

    });

});

It does following things:

  • It verifies the token passed from the request
  • If the token is valid, it retrieves the _id value from the token for the user object
  • Then, it gets the User object which has this _id value from the database
  • If this user exists in the database, it returns a JSON object with the detailed information of this user.

Create our Redux actions to update the app state after a user is authenticated by a JSON Web Token

Now, we have created the route. The next step is to create the Redux actions which can be deployed to update the app state when a user is authenticated via a JSON Web Token.

Let’s add the code below to the ‘client/src/actions.js’ file:

export const authenticateUser = (token) => {

    return dispatch => {
        fetch('http://localhost:3000/authenticate', {
            method: 'post',
            mode: 'cors',
            headers: {
                "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
            },
            body: `token=${token}`
        })
        .then(response => {
            if (response.status >= 200 && response.status < 300) {
                return Promise.resolve(response);
            } else {
                return Promise.reject(new Error(response.statusText));
            }
        })
        .then(response => {
            return response.json();
        })
        .then(data => {
            console.log(data);
            if (data.success === true) {
                // Update the store state
                dispatch(setAuthenticatedUser(data.user));
            }
        })
        .catch(err => {
            console.log(err);
        });
    }
};

The code above is very obvious. It creates an asynchronous action. First, it sends a POST request to the server with the token. Then, if the response from the server is correct, it dispatches the ‘setAuthenticatedUser’ action to update the state of cUser.

Dispatch the action

The action needs to be dispatched automatically every time a page is refreshed no matter what the page is, so the perfect place is the ‘componentDidMount’ event in the ‘App’ component. Let’s add following code to ‘client/App.js’ file.

import { authenticateUser } from './actions';
import { IsEmpty } from './helpers';
componentDidMount() {

    // check if there is a logged in user
    // if there is no logged in user, check to see if there is a token item in localstorage
    // if there is a token item in localstorage, verify if this token is correct and get the its associated user
    if (IsEmpty(this.props.store.getState().cUser)) {

        const token = localStorage.getItem('token');

        if (token !== null) {
            this.props.store.dispatch(authenticateUser(token));
        }

    }

}

The code in the ‘codeDidMount()’ function is executed every time the ‘App’ component is mounted. The Redux store object is passed to the ‘App’ component as a component prop. If the cUser object in the store object is not empty, it dispatches the ‘authenticateUser’ action.

Now, if you refresh the page after you have logged in, you can see you are still logged in.

Let’s call it a day and in the next tutorial in this series, we will work on the logout process. See you next time.

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

×