Websites include both public and private pages. A public page is open to the public, but a private page requires a user login. Authentication can be used to control which users have access to which pages. When a user tries to access a private page before logging in, our React application will need to handle it. We'll need to save the login credentials once they've successfully authenticated.

We'll make a mock API that returns a user token, a login page that fetches the token, and a check for authentication that doesn't require rerouting the user. If a user is not authenticated, we'll provide them the option to log in before allowing them to proceed without having to go to a separate login page. 

Post Graduate Program: Full Stack Web Development

in Collaboration with Caltech CTMEEnroll Now
Post Graduate Program: Full Stack Web Development

What Is ReactJS?

ReactJS is a Javascript library for creating user interfaces that is quite popular and frequently used. Facebook and other individual developers/companies keep it up to date. It is mostly utilized in front-end development and has a stronghold in the web development industry, particularly for single-page user interface development. It's popular among online and mobile app developers because of its quick and simple interactive UI features.

ReactJS is more commonly referred to as React or React.js. ReactJS is preferred by many Fortune 500 companies because of its numerous useful features. It aids developers in creating web pages that can alter data without having to restart the page. Other Javascript libraries, such as Angular JS, Node JS, and Express JS, can be used with it.

Add the Container

Container components in a React app are widely known as the parent elements of other components. They operate as a link between the standard components that render the user interface and the logic that makes the UI interactive and dynamic.

A container component's most typical role is to obtain data. Obtaining data refers to more than just retrieving data from an API's endpoint; it also refers to the logic of a React component. Once the logic is executed or data is obtained in the component, the process is complete. It makes the related component visible. 

Create a new file src/containers/Login.js and add the following.

import React, { useState } from "react";

import Form from "react-bootstrap/Form";

import Button from "react-bootstrap/Button";

import "./Login.css";

export default function Login() {

  const [email, setEmail] = useState("");

  const [password, setPassword] = useState("");

  function validateForm() {

    return email.length > 0 && password.length > 0;

  }

  function handleSubmit(event) {

    event.preventDefault();

  }

  return (

    <div className="Login">

      <Form onSubmit={handleSubmit}>

        <Form.Group size="lg" controlId="email">

          <Form.Label>Email</Form.Label>

          <Form.Control

            autoFocus

            type="email"

            value={email}

            onChange={(e) => setEmail(e.target.value)}

          />

        </Form.Group>

        <Form.Group size="lg" controlId="password">

          <Form.Label>Password</Form.Label>

          <Form.Control

            type="password"

            value={password}

            onChange={(e) => setPassword(e.target.value)}

          />

        </Form.Group>

        <Button block size="lg" type="submit" disabled={!validateForm()}>

          Login

        </Button>

      </Form>

    </div>

  );

}

  • We utilize the useState hook right at the top of our component to save what the user enters in the form. The useState hook simply provides the current value of the variable we wish to store in the state, as well as a function to change it.
  • We then use the setEmail and setPassword procedures to save what the user fills in — e.target.value — and tie the state to our two fields in the form. Our component is re-rendered after we change the state. New values have been assigned to the variables, email and password.
  • The form controls are being adjusted to display the value of our two state variables email and password. A Controlled Component is a pattern in React that displays the current form value as a state variable and sets the new one when the user inputs something.
  • The autoFocus flag is set for our email field, which means that when our form loads, it will focus on this field.
  • We also use a validate method called validateForm to bind our submit button to our state. This merely checks if our fields are not empty, but it could easily be extended to do more.
  • Finally, when the form is submitted, we call our callback handleSubmit. 

Free ReactJS for Beginners Course

Master the Basics of ReactJSStart Learning
Free ReactJS for Beginners Course

Add the Route

In React, React Router is a standard library for routing. It allows users to navigate between views of different components in a React application, change the browser URL, and keep the UI in sync with the URL.

Now, below our home <Route>, we add the following line to src/Routes.js to connect this container to the rest of our project.

<Route exact path="/login">

  <Login />

</Route>

Also, be sure to include our component in the header.

import Login from "./containers/Login";

Login_Page_in_ReactJS_1

Prerequisites

  • We'll need a Node.js development environment; this guide was tested with version 10.22.0 of Node.js and version 6.14.6 of npm.
  • Create React App has been used to set up a React development environment with the non-essential boilerplate eliminated.
  • We'll be utilizing React to get data from APIs. So, we must have good understanding of API.
  • We also must have a good knowledge of HTML, CSS, Javascript before building this project.

Step 1 — Building a Login Page

Create a login page for our application at this stage. Installing React Router and designing components to represent a comprehensive application are the first steps. The login page will then be rendered on any route, allowing our users to log in without being transferred to a new page.

To begin, use npm to install react router. There are two versions available: a browser version and a native version that can be used with React Native. Installing the web version is preferred.

npm install react-router-dom

The package will be installed, and we will be notified when it is finished.

Full Stack Web Developer Course

To become an expert in MEAN StackView Course
Full Stack Web Developer Course

Then, as private pages, add two components called Dashboard and Preferences. These are components that should not be shown to users until they have successfully signed into the application.

To begin, make the following directories:

mkdir src/components/Dashboard

mkdir src/components/Preferences

Then, in a text editor, open Dashboard.js. Nano will be used in this tutorial.

nano src/components/Dashboard/Dashboard.js

Create a< h3> tag in Dashboard.js with the following content:

import React from 'react';

export default function Dashboard() {

  return(

    <h2>Dashboard</h2>

  );

}

Follow the same procedure for Preferences.

After creating some components, we'll need to import them and define routes in App.js.

To get started, open App.js:

nano src/components/App/App.js

Then add the following code to Dashboard and Preferences to import them:

import React from 'react';

import './App.css';

import Dashboard from '../Dashboard/Dashboard';

import Preferences from '../Preferences/Preferences';

function App() {

  return (

    <></>

  );

}

export default App;

Then, from react-router-dom, import BrowserRouter, Switch, and Route.

import React from 'react';

import './App.css';

import { BrowserRouter, Route, Switch } from 'react-router-dom';

import Dashboard from '../Dashboard/Dashboard';

import Preferences from '../Preferences/Preferences';

function App() {

  return (

    <></>

  );

}

export default App;

The next step is to add padding to the main <div> so that our component does not sit directly on the browser's edge. We'll need to update the CSS to accomplish this.

Open App.css

nano src/components/App/App.css

Replace the contents with a.wrapper class with a 20px padding.

.wrapper {

    padding: 20px;

}

The file should be saved and closed. The browser will reload, and we'll see the basic components.

Login_Page_in_ReactJS_2

Login_Page_in_ReactJS_3.

Now, it’s up to us to add some styling which is completely optional.

Check each of the routes, and we will find our dashboard pages.

New Course: Full Stack Development for Beginners

Learn Git Command, Angular, NodeJS, Maven & MoreEnroll Now
New Course: Full Stack Development for Beginners

Step 2 — Creating a Token API

We'll construct a local API to get a user token in this stage. We'll use Node.js to create a mock API that returns a use. \r token. After successfully retrieving the token, we'll use that API from our login page and render the component. By the end of this stage, we'll have a working login page as well as protected sites that can only be accessed after logging.

We'll need a server that will operate as a backend and return the token. Using Node.js and the Express web framework, we can easily establish a server.

To begin, download and install express. Because the server isn't required for the final build, install it as a devDependency.

We'll also have to set up cors. This library will allow all routes to share resources across origins.

npm install --save-dev express cors

We'll get a success message when the installation is finished.

Then, at the root of our application, create a new file named server.js. Because we don't want this file to be part of the final build, don't put it in the /src directory.

nano server.js

Import express, then call express() to create a new app and save the output to a variable called app.

const express = require('express');

const app = express();

Add cors as a middleware after we've finished building the app. Import cors first, then execute the use method on app to add it to the app.

const express = require('express');

const cors = require('cors');

const app = express();

app.use(cors());

Then, using app.use, listen to a certain route. The first argument is the path that the application will listen to, and the second specifies a callback function that will be executed when the path is served. The callback takes two arguments: a req argument that has the request data, and a res argument that holds the result.

A handler for the /login path should be added. With a JavaScript object holding a token, call res.send.

const express = require('express');

const cors = require('cors')

const app = express();

app.use(cors());

app.use('/login', (req, res) => {

  res.send({

    token: 'test123'

  });

});

Last but not least, start the server on port using app.listen.

const express = require('express');

const cors = require('cors')

const app = express();

app.use(cors());

app.use('/login', (req, res) => {

  res.send({

    token: 'test123'

  });

});

app.listen(8080, () => console.log('API is running on http://localhost:8080/login'));

The file should be saved and closed. Start the server in a new terminal window or tab.

node server.js

Login_Page_in_ReactJS_4

We'll get a response that says the server is setting up:

Output:

API is running on http://localhost:8080/login

Full Stack Java Developer Course

In Partnership with HIRIST and HackerEarthEXPLORE COURSE
Full Stack Java Developer Course

Now that our API server is up and running, we must make a request from our login page. Login.js is now open.

nano src/components/Login/Login.js

We gave the Login component a new prop named setToken in the previous step. Destructure the props object to extract the setToken prop and add the PropType from the new prop.

import React from 'react';

import PropTypes from 'prop-types';

import './Login.css';

export default function Login({ setToken }) {

  return(

    <div className="login-wrapper">

      <h1>Please Log In</h1>

      <form>

        <label>

          <p>Username</p>

          <input type="text" />

        </label>

        <label>

          <p>Password</p>

          <input type="password" />

        </label>

        <div>

          <button type="submit">Submit</button>

        </div>

      </form>

    </div>

  )

}

Login.propTypes = {

  setToken: PropTypes.func.isRequired

}

Make a local state to save the Username and Password. Make the <inputs> unregulated components, so we don't have to explicitly specify data. 

import React, { useState } from 'react';

import PropTypes from 'prop-types';

import './Login.css';

export default function Login({ setToken }) {

  const [username, setUserName] = useState();

  const [password, setPassword] = useState();

  return(

    <div className="login-wrapper">

      <h1>Please Log In</h1>

      <form>

        <label>

          <p>Username</p>

          <input type="text" onChange={e => setUserName(e.target.value)}/>

        </label>

        <label>

          <p>Password</p>

          <input type="password" onChange={e => setPassword(e.target.value)}/>

        </label>

        <div>

          <button type="submit">Submit</button>

        </div>

      </form>

    </div>

  )

}

Login.propTypes = {

  setToken: PropTypes.func.isRequired

};

Create a function that performs a POST request to the server. These would be placed in a separate directory in a large application. In this case, we'll add the service to the component directly. 

Free Course: Programming Fundamentals

Learn the Basics of ProgrammingEnroll Now
Free Course: Programming Fundamentals

Create the loginUser async function. The function will take credentials as an input and then use the POST option to call the retrieve method:

import React, { useState } from 'react';

import PropTypes from 'prop-types';

import './Login.css';

async function loginUser(credentials) {

 return fetch('http://localhost:8080/login', {

   method: 'POST',

   headers: {

     'Content-Type': 'application/json'

   },

   body: JSON.stringify(credentials)

 })

   .then(data => data.json())

}

export default function Login({ setToken }) {

...

Finally, construct a handleSubmit form submission handler that will provide the username and password to loginUser. With a successful outcome, call setToken. 

Use the onSubmit event handler on the form to call handleSubmit.

import React, { useState } from 'react';

import PropTypes from 'prop-types';

import './Login.css';

async function loginUser(credentials) {

 return fetch('http://localhost:8080/login', {

   method: 'POST',

   headers: {

     'Content-Type': 'application/json'

   },

   body: JSON.stringify(credentials)

 })

   .then(data => data.json())

}

export default function Login({ setToken }) {

  const [username, setUserName] = useState();

  const [password, setPassword] = useState();

  const handleSubmit = async e => {

    e.preventDefault();

    const token = await loginUser({

      username,

      password

    });

    setToken(token);

  }

  return(

    <div className="login-wrapper">

      <h1>Please Log In</h1>

      <form onSubmit={handleSubmit}>

        <label>

          <p>Username</p>

          <input type="text" onChange={e => setUserName(e.target.value)} />

        </label>

        <label>

          <p>Password</p>

          <input type="password" onChange={e => setPassword(e.target.value)} />

        </label>

        <div>

          <button type="submit">Submit</button>

        </div>

      </form>

    </div>

  )

}

Login.propTypes = {

  setToken: PropTypes.func.isRequired

};

Login_Page_in_ReactJS_5.

Instead of the dashboard, we'll see the login page. After completing and submitting the form, we will be given a web token and redirected to the dashboard page.

We now have a working local API and an application that uses a username and password to request a token. However, there is still an issue. The token is now saved in JavaScript memory, using a local state. We will lose the token if we open a new window, tab, or even just refresh the website, and the user will have to login again.

Step 3 — Storing a User Token with sessionStorage and localStorage

The user token will be saved in this stage. Different token storage methods will be implemented, and the security implications of each solution will be discussed. We'll discover how various ways affect the user's experience when they open new tabs or finish a session.

Tokens can be stored in a variety of ways. There are costs and benefits to each approach. In a nutshell, there are four options: saving in JavaScript memory, sessionStorage, localStorage, and cookie storage. Security is the primary trade-off. Cross-Site Scripting (XSS) attacks can affect any data that is stored outside of the current application's memory. The risk is that if a malicious user is able to load code into our app, it will have access to localStorage, sessionStorage, and any cookies that our app has access to. Non-memory storage solutions provide the advantage of reducing the number of times a user must log in, resulting in a better user experience.

Using localStorage to Save Data Across Windows

LocalStorage, unlike sessionStorage, saves data long after the session has ended. This is more convenient because it allows users to open several windows and tabs without having to log in again, but it has certain security issues. Even if the user closes the browser, they will stay logged in to the program if they share their computer. The user will be responsible for explicitly logging out. Without requiring a login, the next user would have immediate access to the application. It's a risk, but for some applications, the convenience may be worth the risk.

Open useToken.js to convert to localStorage.

nano src/components/App/useToken.js

After that, replace any references to sessionStorage with localStorage.  We'll use the same procedures as before.

import { useState } from 'react';

export default function useToken() {

  const getToken = () => {

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

    const userToken = JSON.parse(tokenString);

    return userToken?.token

  };

  const [token, setToken] = useState(getToken());

  const saveToken = userToken => {

    localStorage.setItem('token', JSON.stringify(userToken));

    setToken(userToken.token);

  };

  return {

    setToken: saveToken,

    token

  }

}

Login_Page_in_ReactJS_6

Save the document. The browser will reload after the document is saved. Because there is no token in localStorage yet, we will need to log in again, but once we do, we will remain logged in when we open a new tab.

We used sessionStorage and localStorage to save tokens in this stage. We also made a custom Hook to make a component re-render happen and to shift component logic to a different function. We also learned how sessionStorage and localStorage affect the ability of a user to start new sessions without logging in.

If you're eager to gain the skills required to work in a challenging, rewarding, and dynamic IT role - we've got your back! Discover the endless opportunities through this innovative Post Graduate Program in Full Stack Web Development course designed by our partners at Caltech CTME. Enroll today!

Conclusion

Both public and private pages can be found on a website. A public page is accessible to anybody, but a private page necessitates a user login, so keeping that in mind, authentication is one of the most important feature of any website. The combination of security issues and user experience can be daunting, but if we concentrate on validating data and rendering components at the appropriate times, it can become a simple procedure.

If you are interested in learning more about ReactJS and other related concepts, you can enroll in Simplilearn’s exclusive Full Stack Web Development Certification Course and accelerate your career as a software developer. The program comprises a variety of software development courses, ranging from the fundamentals to advanced topics. 

Simplilearn also offers free online skill-up courses in several domains, from data science and business analytics to software development, AI, and machine learning. You can take up any of these courses to upgrade your skills and advance your career.

About the Author

SimplilearnSimplilearn

Simplilearn is one of the world’s leading providers of online training for Digital Marketing, Cloud Computing, Project Management, Data Science, IT, Software Development, and many other emerging technologies.

View More
  • Disclaimer
  • PMP, PMI, PMBOK, CAPM, PgMP, PfMP, ACP, PBA, RMP, SP, and OPM3 are registered marks of the Project Management Institute, Inc.