Setting up Authentication in Reactjs

With the advancement in web technologies especially JavaScript, Single Page Application (SPA) have become more popular and prominent way of web application development. Single Page Application are usually associated with JavaScript Framework such as Angular, VueJS, ReactJS and so on.

Authentication on SPAs can be a tricky considering the various methods of authentication at our disposal such as Auth0 (which is an Auth-as-a-service platform), njwt, Okta. amongst others. For the purpose of this article i have chosen JsonWebToken(JWT).

In this article we would be Using ReactJS and ExpressJS to show how to manage React authentication in SPAs. We would be creating a developer tips application that allows every user view regular tips and allow only authenticated users view special tips.

Below is the React authentication app we are going to build.

React authentication

React authentication app

Configuring React Development Environment

For this tutorial I am using Eclipse IDE with CodeMix Installation.

What is CodeMix?

Quoting the CodeMix website:

CodeMix is an Eclipse plugin that gives you access to a wide array of technologies from VS Code itself and add-on extensions built for Code OSS, directly from Eclipse. You’ll enjoy all the enterprise tooling you depend on, like Java and Spring, plus access to all those cool web languages and exciting new technologies you’ve been wanting to try.

It’s the best of both worlds! Coding without limits. Keep coding those awesome enterprise apps, and throw in impressive new stuff—with amazing extensions for languages like Python, PHP, and TypeScript, as well as frameworks like Vue, React and Angular. Or, why not dockerize your app? The possibilities are endless!

Get Codemix For Free

We are going to take advantage of the CodeMix in this tutorial. CodeMix becomes an unparalleled ally when it comes to code completion and visual assistance for debugging and building our Reactjs app.  CodeMix will easily configure the working environment, leading to better productivity. So let’s get started with the React authentication tutorial

Creating a React Project using CodeMix

We’ll be using the latest version of all the tech libraries and stacks as at the time of this writing. To create a new React Project Navigate to File-> New -> Project.

creating new project in codemix

creating new project in CodeMix

Now click next and enter your project name in the next Screen, Finally, click Finish to continue with React project creation. we will now open a terminal for a project using the command palette by pressing  Ctrl + Shift + P.  then Type terminal: create a new integrated terminal & select project from drop down. Terminal will automaticlaly be opened in the project folder.

React authentication

opening a terminal for a project using the command palette

  • Once you are inside the Terminal, execute: npm install
  • Finally, run the React App by excuting run npm run start
  • To see the React App Running live, Open your browser on http://localhost:3000

Install Nodemon

To listen for changes on the API development and restart the server accordingly. This is only required for development purpose and is not needed for production. It is installed using npm as well.

npm install -g nodemon

After that has been completed, we make some changes to the folder structure. First we add an api folder in the root folder of our project. This folder would house the express backend (which include the server.js file). Then we create a components folder in the src folder, this would house all our ReactJS components.

After all that has been done we would then update our package.json file to include the following packages ExpressJS, axios, react-router-dom, jsonwebtoken, cors, and body-parser.

npm install --save express react-route-dom axios jsonwebtoken cors body-parser
  • Axios – It is a Promise based HTTP client for the browser and NodeJS. we would be using it to fetch data for our ReactJS frontend.
  • JsonWebTokem(JWT) – It is a compact and self-contained way for securely transmitting information between parties as a JSON object.

After including those packages, we include a script to run the API. The package.json should look like this:

{
 "name": "dev-tips",
 "version": "0.1.0",
 "private": true,
 "dependencies": {
 "axios": "^0.17.1",
 "body-parser": "^1.15.2",
 "cors": "^2.8.1",
 "express": "^4.14.0",
 "jsonwebtoken": "^8.1.0",
 "react": "^16.3.2",
 "react-dom": "^16.3.2",
 "react-router-dom": "^4.2.2",
 "react-scripts": "1.1.4"
 },
 "scripts": {
 "start": "react-scripts start",
 "build": "react-scripts build",
 "test": "react-scripts test --env=jsdom",
 "eject": "react-scripts eject",
 "api": "nodemon api/server.js"
 }
 }

 

After all the update to the package.json file we can run our ReactJS using npm start.

Development

We would first develop the API which would be consumed by the ReactJS frontend. This is a better option than developing the ReactJS Frontend while assuming the response from the API.

Developing the Express Backend

We first create our server.js file in the api folder.

Creating a New file in Codemix

Creating a New file in Codemix

This file will include the ExpressJS setup and should look like this:

'use strict';

const express = require('express');
 const app = express();
 const jwt = require('jsonwebtoken');
 const cors = require('cors');
 const bodyParser = require('body-parser');
 const data = require('./data');
 const middleware = require('./middleware');

app.use(bodyParser.json());
 app.use(bodyParser.urlencoded({ extended: true }));
 app.use(cors());

app.get('/api/tips/regular', (req, res) => {
 res.json(data.regular);
 });

app.get('/api/tips/special', middleware, (req,res) => {
 res.json(data.special);
 });

app.post('/api/auth', (req,res) => {
 let user = data.users.filter((user) => {
 return user.name == req.body.name && user.password == req.body.password;
 });
 if (user.length){
 let token_payload = {
 name: user[0].name,
 password: user[0].password
 };
 // create a token using user name and password vaild for 2 hours
 let token = jwt.sign(
 token_payload,
 "jwt_secret_password",
 { expiresIn: '2h' }
 );
 let response = {
 message: 'Token Created, Authentication Successful!',
 token: token
 };

// return the information including token as JSON
 return res.status(200).json(response);

} else {
 return res.status("409")
 .json("Authentication failed. admin not found.");
 }
 });
 let port = 3100;
 app.listen(port);
 console.log('api runnging on port '+ port +': ');


The server.js file sets up the API and it provides three routes which includes:

  • The Regular tips route to provide regular tips to both authenticated and unauthenticated users from the data.js file (which we would soon create).
  • The Authentication Route which authenticates users if they are found in the list of provided users from data.js. It returns a token generated from the token payload (This usually include the user information such email, name, phone number and others).
  • The Special tips route to provides special tip to only authenticated users. It uses a middleware which expects valid token to be provided before it grants access. The middleware is written in the middleware.js file in the api folder. middleware.js should contain the code below.
 const jwt = require('jsonwebtoken');

const JWT_SECRET = "jwt_secret_password";

module.exports = (req, res, next) => {

// check header or url parameters or post parameters for token
 var token = req.body['x-access-token'] || req.query['x-access-token'] || req.headers['x-access-token'];

// decode token
 if (token) {

// verifies secret and checks exp
 jwt.verify(token, JWT_SECRET, function(err, decoded) {
 if (err) {
 return res.status(403).send({
 success: false,
 message: 'Failed to authenticate token.'
 });
 } else {
 // if everything is good, save to request for use in other routes
 req.decoded = decoded;
 next();
 }
 });
 } else {
 // if there is no token, return an error
 return res.status(403).send({
 success: false,
 message: 'No token provided.'
 });
 }
 };

The data.js file which acts as the data source for our application and should look like this:

 const regular = [
 {
 id: 01,
 content: 'Lorem ipsum dolor sit amet, iusto appellantur vix te, nam affert.'
 },
 {
 id: 02,
 content: 'Lorem ipsum dolor sit amet, iusto appellantur vix te, nam affert.'
 },
 {
 id: 03,
 content: 'Lorem ipsum dolor sit amet, iusto appellantur vix te, nam affert.'
 },
 {
 id: 04,
 content: 'Lorem ipsum dolor sit amet, iusto appellantur vix te, nam affert.'
 },
 {
 id: 05,
 content: 'Lorem ipsum dolor sit amet, iusto appellantur vix te, nam affert.'
 }
 ];
 const special = [
 {
 id: 11,
 content: 'Epicuri assentior sed id, ne tota altera alterum vim. Cum viris.'
 },
 {
 id: 12,
 content: 'Epicuri assentior sed id, ne tota altera alterum vim. Cum viris.'
 },
 {
 id: 13,
 content: 'Epicuri assentior sed id, ne tota altera alterum vim. Cum viris.'
 },
 {
 id: 14,
 content: 'Epicuri assentior sed id, ne tota altera alterum vim. Cum viris.'
 },
 {
 id: 15,
 content: 'Epicuri assentior sed id, ne tota altera alterum vim. Cum viris.'
 },
 {
 id: 16,
 content: 'Epicuri assentior sed id, ne tota altera alterum vim. Cum viris.'
 }
 ];

const users = [
 {
 'name': 'user', 'password': 'qwerty'
 },
 {
 'name': 'example', 'password': 'qwerty'
 }
 ]

module.exports = { regular: regular, special: special, users: users }

 

Now that we have done the all the setup for the API, we can now start up the server using the script we added in package.json. We do that by running the command below on another terminal since the ReactJS frontend is currently using the initial terminal.

npm run api

Now we can test our api using Postman. This is a best useful tool for test your API and looks at the response you get from your server. For example:

ReactJS Frontend

Now that the API is ready. we can now begin to code our ReactJS components. We would be building The App component which the root component which will house our nav bar (which contains the links) and also our application body which is determine by the Route element (display the components for the respective link). Since we are using react-router-dom as our router we have to enclose all Link and Route element in a Router (originally named BrowserRouter) element. The App.js file should look like this.

IntelliSense in Codemix

IntelliSense in Codemix

Tip: CodeMix supports word based completions for any programming language. IntelliSense offers different types of completions, including language server suggestions, snippets, and simple word based textual completions.

import React, { Component } from 'react';
import Login from './components/login';
import Regular from './components/regular';
import Special from './components/special';
import Home from './components/home';
import {  BrowserRouter as Router, Link, Route } from 'react-router-dom';
import { isAuthenticated } from './respository';


class App extends Component {

  logOut(){
    localStorage.removeItem('x-access-token');
  }
  
  render() {
    return (
      <Router>
      <div>
        <nav className="navbar navbar-default">
          <div className="container-fluid container">
            <div className="navbar-header">
              <span className="navbar-brand"><Link to="/"> DevTip</Link></span>
            </div>
            <ul className="nav navbar-nav">
              <li>
                <Link to="/">Home</Link>
              </li>
              <li>
                <Link to="/tip/regular">Regular Tips</Link>
              </li>
              <li>
                {
                 ( isAuthenticated() ) ? <Link to="/tip/special">Special Tips</Link>:  ''
                }
              </li>
            </ul>
            <ul className="nav navbar-nav navbar-right">
             {
               ( isAuthenticated() ) ? 
                ( <li onClick={this.logOut}><a href="/">Log out</a> </li>) : 
                ( <li><Link to="/login">Log in</Link></li> )
             }
            </ul>
          </div>
        </nav>
        <Route exact path="/" component={Home} />
        <Route exact path="/tip/regular" component={Regular} />
        <Route exact path="/tip/Special" component={Special} />
        <Route exact path="/login" component={Login} />
      </div>
      </Router>
    );
  }
}

export default App;

 

The isAuthenticated function checks to see if the user is authenticated and return a boolean to that effect. Hence The Special tips Link and log out Link is only shown to authenticated users while the log in Link is shown to only unauthenticated users.

The isAuthenticated function is housed in a repository.js file (in the src folder), which stores functions needed by the various react component we would be creating.

Press Ctrl+P to use Quick Open to open any file by its name.

Press Ctrl+P to use Quick Open to open any file by its name.

The repository.js contains the method which communicates with the API using axios. It provide methods such as:

  • getRegularTips – which fetches the regular tips from the API.
  • login – which sends a post request with user information and get an access token on success. It then stores the access-token and it’s expiration time (which is 2 hours after receipt of the token) on localStorage.
  • getSpecialTips – which fetches the special tip for only authenticated users.
If you press Ctrl and hover over a symbol, a preview of the declaration appears in Codemix

If you press Ctrl and hover over a symbol, a preview of the declaration appears in Codemix

The repository.js file should then contain the code below:

import axios from 'axios';

const BASE_URL = 'http://localhost:5000';

export function getRegularTips () {
 return axios.get(`${BASE_URL}/api/tips/regular`)
 .then(response => response.data);
 }

export function getSpecialTips () {
 return axios.get(`${BASE_URL}/api/tips/special`, { params: { 'x-access-token': localStorage.getItem('x-access-token')} })
 .then(response => response.data)
 .catch(err => Promise.reject('Request Not Authenticated!'));
 }

export function login (data) {
 return axios.post(`${BASE_URL}/api/auth`, { name: data.name, password: data.password })
 .then(response => {
 localStorage.setItem('x-access-token', response.data.token);
 localStorage.setItem('x-access-token-expiration', Date.now() + 2 * 60 * 60 * 1000);
 return response.data
 })
 .catch(err => Promise.reject('Authentication Failed!'));
 }

export function isAuthenticated(){
 return localStorage.getItem('x-access-token') && localStorage.getItem('x-access-token-expiration') > Date.now()
 }

Now we can start building the other set of component which includes the following components:

  • Home Component– This component handle the what is to be displayed on the homepage. It is created in the components folder as home.js and should contain the code below.
import React, { Component } from 'react';

class Home extends Component {

render() {

return (
 <div>
 <hr/>
 <h1 className="text-center">Welcome Developer Tips</h1>
 <div className="">
 <h3 className="text-center">Get New Tips here</h3>
 </div>
 <hr/>
 </div>
 );
 }
 }

export default Home;

Regular Component– This component is responsible for handling the display of regular tip for public viewing. It is created in the components folder as regular.js and should contain the code below.

import React, { Component } from 'react';
import { getRegularTips } from '../respository';

class Regular extends Component {

  constructor() {
    super();
    this.state = { tips: [] };
  }

  componentDidMount() {
    getRegularTips().then((tips) => {
      this.setState({ tips });
    });
  }

  render() {

    return (
      <div>
        <h3 className="text-center">Regular Developer Tips</h3>
        <hr/>
        { 
          this.state.tips.map((tip) => (
              <div className="col-sm-10 col-sm-offset-1" key={tip.id}>
                <div className="panel panel-default">
                  <div className="panel-heading">
                    <h3 className="panel-title"> <span className="btn">#{ tip.id }</span></h3>
                  </div>
                  <div className="panel-body">
                    <p> { tip.content } </p>
                  </div>
                </div>
              </div>
          ))
        }
      </div>
    );
  }
}

export default Regular;

 

It Fetches the data to be displayed by calling the getRegularTips function after the component has been mounted (i.e it uses calls the getRegularTips function in the ReactJs componentDidMount event handler). It then stores the data which is an array of tips objects in the component state then displays the data by iteration through the tips array.

  • Login Component – This component is responsible for submit the user information to the API and getting the token. It is created as login.js in the components folder with and contains the code below.
import React, { Component } from 'react';
 import { login } from '../respository';

class Login extends Component {
 constructor() {
 super();
 this.state = { name: '', password: '' };
 this.handleInputChange =this.handleInputChange.bind(this);
 this.submitLogin =this.submitLogin.bind(this);
 }

handleInputChange(event) {
 this.setState({[event.target.name]: event.target.value})
 }

submitLogin(event){
 event.preventDefault();
 login(this.state)
 .then(token => window.location = '/')
 .catch(err => alert(err));
 }

render() {
 return (
 <div className="container">
 <hr/>
 <div className="col-sm-8 col-sm-offset-2">
 <div className="panel panel-primary">
 <div className="panel-heading">
 <h3>Log in </h3>
 </div>
 <div className="panel-body">
 <form onSubmit={this.submitLogin}>
 <div className="form-group">
 <label>Name:</label>
 <input type="text" className="form-control" name="name"
 onChange={this.handleInputChange}/>
 </div>
 <div className="form-group">
 <label>Password:</label>
 <input type="password" className="form-control" name="password"
 onChange={this.handleInputChange}/>
 </div>
 <button type="submit" className="btn btn-default">Submit</button>
 </form>
 </div>
 </div>
 </div>
 </div>
 );
 }
 }
 export default Login;

The handleInputChange gets the user input and save them to the component state using the input field name which corresponds to the initial state attributes (name and password).
The submitLogin handles the on submit event of the login form. It calls the login function from repository.js responds to its response. In the event of a failed login attempt, it alerts the user while in the event of a successful login attempt redirect the user to home with the access token saved in localStorage hence revealing and unblocking the special tips link.

  • Special Component – This component is responsible for loading the Special tips for authenticated users only. It is created as special.js in the components folder with and contains the code below.
import React, { Component } from 'react';
 import { getSpecialTips, isAuthenticated } from '../respository';
 import { Redirect } from 'react-router-dom';

class Special extends Component {

constructor() {
 super();
 this.state = { tips: [], auth: true };
 }

componentDidMount() {
 if( isAuthenticated() )
 getSpecialTips().then((tips) => {
 this.setState({ tips });
 }).catch(err => {
 alert('User Not Authenticated');
 this.setState({auth: false}
 )})
 else{
 alert('User Not Authenticated');
 this.setState({auth: false})
 }
 }

render() {

return (
 <div>
 {(this.state.auth) ? '' : <Redirect to="/" />}
 <h3 className="text-center">Special Developer Tips</h3>
 <hr/>
 {
 this.state.tips.map((tip) => (
 <div className="col-sm-10 col-sm-offset-1" key={tip.id}>
 <div className="panel panel-success">
 <div className="panel-heading">
 <h3 className="panel-title">
 <span className="btn">#{ tip.id }</span></h3>
 </div>
 <div className="panel-body">
 <p> { tip.content } </p>
 </div>
 </div>
 </div>
 ))
 }
 </div>
 );
 }
 }
 export default Special;

It is similar to the Regular Component with a few changes. After the Component has been mounted it checks to see if the user has been authenticated using the isAuthenticated function. If the user is authenticated (that is if there is a token in the localStorage and it hasn’t expired) the request proceeds to the API server where further checks on the authentication is carried out. If it passes the API server authentication the tips array data is stored to the state tips attribute and is then rendered. If Authentication fails at any point in this component the user is alerted and then redirected to the home page.

After we have successfully created all the ReactJS components for corresponding each of the route. Do not forget to run the API server using the instruction below on another terminal while running the Application.

npm run api

Our Application is ready to Use, Yay!

Conclusion

In this article we created an ExpressJS API with jsonwebtoken used for authentication. We have learnt to build our authentication middleware and attach it to various routes. Although This is not a by any mean a production ready API, as most of our keys are stored within the application definition rather than a .env file (usually accessed by using the dotenv package), our API routes are declared within server.js file rather than being abstracted to separate routes.js file and other production practices.

We took advantage of the CodeMix in the tutorial. CodeMix  powerful features boosted our productivity and provided code completion and visual assistance for debugging and building our Reactjs app.

Get Codemix For Free

We have also learnt how to authenticate a user request using ReactJS with the help of localStorage. Although one might be tempted to use jsonwebtoken to check the token’s validity i advise against it as it open the system to vulnerability as programmers can not fully protect what goes on the client system as the JWT_SECRET may be exposed.

This code for this application is available on GitHub.  To provide suggestions or feedback, or see updates before the general public, do join  CodeMix Insiders program, on Slack or Facebook

About the author

Deven Rathore

I'm Deven Rathore, a multidisciplinary & self-taught designer with 3 years of experience. I'm passionate about technology, music, coffee, traveling and everything visually stimulating. Constantly learning and experiencing new things.

Pin It on Pinterest