Add User Authentication To Your React App

Add User Authentication To Your React App
Add User Authentication To Your React App

When building web applications, user authentication is a necessary feature to add to your application. User authentication allows verified users to access features that are available to them in your application and denies unverified users access.

Integrating user authentication in React applications is no easy task and is time-consuming since it involves several complex processes, such as hashing passwords, generating and managing authentication tokens, and many more.

However, with platforms like Back4App, integrating user authentication with your React applications is simple and easy. In this article, you’ll explore how to add user authentication to your React app using Back4App.

Understanding User Authentication

User authentication is the process of determining the identity of a person who is trying to get access to your application.

It requires that the person trying to gain access to your application provides verifiable credentials, which can be in the form of a username and password, biometric data, access keys/tokens, etc.

If your authentication mechanism deems the credentials valid, it grants the user access to your application, else it rejects the authentication request with the appropriate error message.

Common Authentication Methods

Some common authentication methods include the following:

  • Username and Password: This authentication method requires a user to provide a valid username and a password in order to authenticate their identity. After a user provides a username and password, this authentication mechanism compares them with the data stored in the database and only approves the authentication request if they match.
  • Multi-Factor Authentication (MFA): MFA refers to the combination of multiple authentication mechanisms to authenticate a user. In an authentication system using MFA, the user is required to validate their identity more than once. For example, a user may be required to enter an authentication code after authenticating their identity using a username and password.
  • Biometric Authentication: Biometric authentication refers to authentication mechanisms that depend on biological features such as face, iris patterns, fingerprints, or voice recognition to authenticate a user.
  • OAuth: OAuth is an authentication protocol that allows your users to authenticate their identities by granting your application access to their preferred OAuth provider such as Facebook or X (formerly Twitter). When a user tries to authenticate using OAuth, they will be redirected to the OAuth provider’s login screen to authenticate their identity.
  • JSON Web Tokens (JWT): JWTs are portable and URI-safe compact token formats that are usually used to transmit authentication and authorization details securely between different parties. Authentication with JWTs involves the user sending the JWT access token in their requests to validate their identity.

Adding User Authentication to your Application with Back4app

Back4app is a cloud platform that offers a wide range of services, including a Backend-as-a-service (BaaS) feature. Back4app’s BaaS option offers various features, including user authentication.

Implementing user authentication with Back4app involves a few steps. For this tutorial, you will build a simple bank management app using React.

This app will integrate the Back4app authentication features, allowing you to create an account, log in to your account, access your current login information, and log out.

Creating a React Application

To create a new React application, start by scaffolding a new React app using Vite. Vite is a web development build tool that provides a faster and more efficient development experience.

Run the following command in your terminal to scaffold the new React app:

npm init vite

After running the command above, a series of prompts will display on your screen, asking you to specify the application’s name, the framework of choice, and the language variant to use.

Like so:

Create React App with Vite

In the image above, the application’s name is react-authentication-app, the selected framework is React, and the language variant is JavaScript.

Next, change your current directory to the application’s directory and install the necessary dependencies by running the commands below:

cd react-authentication-app && npm install

Next, install the React Router in your application by running the command below:

npm install react-router-dom

Next, open your React application on an IDE (Integrated Development Environment) and modify the main.jsx file by replacing the contents with the code block below.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import { BrowserRouter } from 'react-router-dom'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
)

This code block imports the BrowserRouter component from the react-router-dom package and wraps the App component.

This enables routing in the entire application. In your App component, define the different routes in the application.

Like so:

import { Route, Routes } from "react-router-dom";
import Signup from "./pages/Signup";
import Login from "./pages/Login";
import Home from "./pages/Home";

function App() {

  return (
    <>
      <Routes>
        <Route path="/home" element={<Home />} />
        <Route path="" element={<Signup />}/>
        <Route path="/login" element={<Login />}/>
      </Routes>
    </>
  )
}

export default App

The code block above defines three separate routes, allowing users to navigate between the sign-up, login, and home pages based on the URL paths.

Now, create a components folder in the application’s src directory. In this folder, make an Authnavbar.jsx file. This component will contain a navigation bar that will display on the sign-up and login pages.

In the Authnavbar component, write this code:

import React from "react";
import { Link } from "react-router-dom";

function Authnavbar(props) {
  return (
    <nav>
      <h3>Banka</h3>
      {props.type === "signup" ? (
        <div>
          If you already have an account{""}
          <Link to="/login" className="link">
            Log in
          </Link>
        </div>
      ) : props.type === "login"? (
        <div>
          If you don't have an account{""}
          <Link to="/" className="link">
            Sign Up
          </Link>
        </div>
      ) : null}
    </nav>
  );
}

export default Authnavbar;

The code block above creates a navigation bar with conditional rendering based on the value of the type prop, allowing users to navigate between the sign-up and login pages within the application.

Next, generate a pages folder in the application’s src directory. In this folder, create three components: Home.jsx, Sign-up.jsx, and Login.jsx. These files will serve as the home, sign-up, and login pages.

The Sign-up page will contain a form where the users can input their details, such as a username, email, and password.

For example:

import React from "react";
import Authnavbar from "../components/Authnavbar";

function Signup() {
  const [formData, setFormData] = React.useState({
    username: "",
    email: "",
    password: "",
  });

  const handleChange = (event) => {
    setFormData((prevState) => ({
      ...prevState,
      [event.target.name]: event.target.value,
    }));
  };

  return (
    <>
      <Authnavbar type= "sign-up"/>
      <form>
        <input
          type= "text"
          name= "username"
          placeholder= "Username..."
          onChange={handleChange}
          value={formData.username}
        />
        <input
          type= "email"
          name= "email"
          placeholder= "Email..."
          onChange={handleChange}
          value={formData.email}
        />
        <input
          type= "password"
          name= "password"
          placeholder= "Password..."
          onChange={handleChange}
          value={formData.password}
        />
        <div>
          <button>Sign Up</button>
        </div>
      </form>
    </>
  );
}

export default Sign-up;

This code block represents a sign-up page. It contains a form with input fields, including the user’s username, email, and password.

There is also a formData state that stores the values of the username, email, and password input fields. The handleChange function updates the formData state whenever the user types in the input fields.

In the Login component, write the following lines of code:

import React from "react";
import Authnavbar from "../components/Authnavbar";

function Login() {
  const [formData, setFormData] = React.useState({
    username: "",
    password: "",
  });

  const handleChange = (event) => {
    setFormData((prevState) => ({
      ...prevState,
      [event.target.name]: event.target.value,
    }));
  };

  return (
    <>
      <Authnavbar type= "login"/>
      <form>
        <input
          type= "text"
          name= "username"
          placeholder= "Username..."
          onChange={handleChange}
          value={formData.username}
        />
        <input
          type= "password"
          name= "password"
          placeholder= "Password..."
          onChange={handleChange}
          value={formData.password}
        />
        <div>
          <button>Log In</button>
        </div>
      </form>
    </>
  );
}

export default Login;

The login page is quite similar to the sign-up page, except there is no email input field in the login form, and the formData object state lacks the email property.

Now, style your application by replacing the code in your index.css file with the following lines of code:

*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body{
  color: #333333;
  background-color: #E8F7F5;
  inline-size: 50%;
  margin: auto;
  font-family: "Montserrat", sans-serif;
}

input{
  padding: 0.7rem 1rem;
  border-radius: 10px;
  border: none;
  outline: none;
  color: #333333;
  background-color: #FFFFFF;
  font-family: "Montserrat", sans-serif;
  inline-size: 100%;
}

input::placeholder{
  font-family: "Montserrat", sans-serif;
  font-weight: 500;
}

button{
  padding: 0.7rem 1rem;
  background-color: #00C3A5;
  color: #FFFFFF;
  font-family: "Montserrat", sans-serif;
  font-size: 14px;
  border-radius: 7px;
  border: none;
}

form{
  margin-block-start: 25%;
  display: flex;
  flex-direction: column;
  gap: 2rem;
  align-items: center;
}

nav{
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem 0;
}

.link{
  text-decoration: none;
  color: #00C3A5;
}

After styling your application, run the command below to start your application:

npm run dev

Running the command above will provide you with the link http://localhost:5173/. Navigate to this link on your web browser to view your application.

With the styles you defined, the sign-up page should look like the page below:

Banka Sign Up Page

Your login page should look like the image below:

Banka Log In Page

Now that you have created your React application, you need to create a new Back4app application to handle your authentication.

Creating a New Back4App Application

To use Back4app, you need to have an account. You can sign up for free if you do not have one.

For this tutorial, you will create a new Back4app application using the Back4app AI agent. The Back4app AI Agent is an AI helper that helps you execute tasks on Back4app using prompts.

Sign in to your Back4app account and locate and click the “AI Agent” button on your dashboard’s navbar (See image below).

Back4app Home Dashboard

To use the Back4app AI Agent to generate an application, enter the prompt below:

create a new application named react-authentication

You should get a response similar to the one shown in the image below:

Create new Back4app App with the AI Agent

As shown in the image above, the Back4app AI Agent created an application named react-authentication. You can find this application on the “My App” web page.

Alternatively, you can create a new Back4app application using Back4app’s intuitive UI (User Interface).

In the react-authentication app on Back4app, create a wallet class. This wallet class will hold valuable information about the user’s wallet.

You can create the class with the AI Agent using the prompt below:

Create a Wallet class and add the following fields: balance, owner(relation to the User class indicating which user owns the wallet), createdAt, and updatedAt in the react-authentication app

The AI Agent will initiate the process of creating the wallet class and add the specified, as shown in the image below.

Create Wallet Class with AI Agent

Connecting Your React App to Back4app

After creating your Back4app application, you need to connect your Back4app application to your React app using the Parse SDK.

Install Parse in your application by running the command below:

npm install parse

Next, initialize it in your application by importing the Parse object in your app component and configuring it with your Back4app application keys.

Like so:

import Parse from 'parse';

Parse.initialize('YOUR_APP_ID', 'YOUR_JAVASCRIPT_KEY');
Parse.serverURL = '<https://parseapi.back4app.com/>';

Replace 'YOUR_APP_ID' and 'YOUR_JAVASCRIPT_KEY' with the respective keys provided by Back4App. You’ll find your app keys on the screen after creating your app with the Back4app AI Agent.

App Credentials

Note that you should always secure the keys properly in your application, preferably using env variables.

Implementing User Authentication using the Parse SDK

With Parse initialized, you can now implement user authentication in your React application.

Back4App automatically provides a User class for user authentication. This class has default fields like username, password, email, etc.

Add the following lines of code to the Sign-up component to implement user sign-up in your React application.

import Parse from "parse"
import { useNavigate } from "react-router-dom";

const navigate = useNavigate();

const signUpUser = async () => {
  try {
    const newUser = new Parse.User();
    newUser.set("username", formData.username);
    newUser.set("email", formData.email);
    newUser.set("password", formData.password);
    const user = await newUser.signUp();

    let Wallet = Parse.Object.extend("Wallet");
    let wallet = new Wallet();

    let userPointer = {
      __type: "Pointer",
      className: "_User",
      objectId: user.id,
    };

    wallet.set("owner", userPointer);
    wallet.set("balance", 100);
    wallet.save();

    console.log(`${user} have successfully signed up`);
    navigate("/login");
  } catch (error) {
    console.log("you encountered an error signing up");
  }
};

The code block above uses the useNavigate hook from react-router-dom to programmatically navigate to different routes. The useNavigate hook returns a navigate function assigned to the variable navigate.

The signUpUser function is responsible for signing up a new user. The function creates a new Parse User object newUser using the Parse.User method and sets the values of its username, email, and password properties to the values of the corresponding properties of the formData object state. It then calls the signUp method asynchronously on the newUser object.

After the code creates a new user, it defines a Parse class named “Wallet” with Parse.Object.extend method.

Then, it creates a userPointer object, which represents a pointer to the user with the class name _User and the newly signed-up user’s object ID. Using the set method, the code sets the wallet’s owner to the userPointer object and sets the wallet’s initial balance to 100.

On a successful sign-up, the function logs a success message and redirects to the login route using the navigate function. If an error occurs during sign-up, it catches it and logs an error message.

Next, create a handleSubmit function that invokes the signUpUser function. Bind this function to the sign-up form using the onSubmit event handler.

This ensures that when a user submits the sign-up form, the handleSubmit function is automatically triggered.

The handleSubmit function should look like this:

const handleSubmit = (event) => {
  event.preventDefault();
  signUpUser();
};

To implement the user login, you will create a logInUser function that holds the logic for logging in a user.

Add the code block below to your Login component:

import Parse from "parse"
import { useNavigate } from "react-router-dom";

const navigate = useNavigate();

const logInUser = async () => {
    try {
      const user = await Parse.User.logIn(formData.username, formData.password);
      console.log("User logged in successfully:", user);
      navigate("/home");
    } catch (error) {
      console.log("Error logging in user:", error);
    }
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    logInUser();
  };

The logInUser function calls the logIn method of the Parse.User class asynchronously, passing in the values of the username and password properties from the formData object state.

The function checks if the username and password the user entered match those stored in Back4App’s User class. If they match, the user will log in successfully.

Bind the handleSubmit function to the login form using the onSubmit event. The handleSubmit function will call the logInUser function and attempt to log the user in.

After login, the Parse SDK manages the user’s session. You can access the current user with the Parse.User.current method. You will make use of this method in the Home component.

Open the Home component and write this code:

import React from "react";
import Parse from "parse";
import { useNavigate } from "react-router-dom";

function Home() {
  const navigate = useNavigate();
  
  const [balance, setBalance] = React.useState(0);

  const user = Parse.User.current();

  const fetchBalance = async () => {
	  const Wallet = await Parse.Object.extend("Wallet");
	  const query = new Parse.Query(Wallet);
	  query.equalTo("owner", user);
	  const wallet = await query.first();
	  setBalance(wallet.get("balance"));
};

fetchBalance();

  return (
    <>
      <nav>
        <h1 className="title">Banka</h1>
        <button>Log out</button>
      </nav>
      <div className="home">
        <p className="top">Welcome {user.get("username")}</p>

        <div className="balance-card">
          <p>Total Wallet Balance</p>
          <h1>{balance}</h1>
          <button>Top Up</button>
        </div>

        <div className="features">
          <div className="card">
            <p>Pay Bills</p>
          </div>
          <div className="card">
            <p>Airtime/Data</p>
          </div>
          <div className="card">
            <p>Transfers</p>
          </div>
          <div className="card">
            <p>Withdraw</p>
          </div>
        </div>
      </div>
    </>
  );
}

export default Home;

This code block above represents the home page of your application. The fetchBalance function fetches the current user’s wallet balance.

It queries the “Wallet” class with the Parse.Query method and sets a constraint on the query to retrieve the wallet where the owner property equals the current user using the query.equalTo method.

The query.first method executes the query and returns the first result that matches the query constraints.

With the setBalance function, the code sets the wallet balance to the balance state. The code displays the user’s name and their wallet balance.

In the JSX section of the code block, you will find the “top up” button in the div tag with the class balance-card.

Clicking on this button should increase the wallet balance by 10. To make this possible, you will create a topUp function containing the logic for increasing the balance.

Like so:

const topUp = async () => {
  const Wallet = await Parse.Object.extend("Wallet");
  const query = new Parse.Query(Wallet);
  query.equalTo("owner", user);
  const wallet = await query.first();
  wallet.increment("balance", 10);
  const newBalance = await wallet.save();
  setBalance(newBalance.get("balance"));
};

This code fetches the user’s wallet from the Parse backend, increments the balance by 10 with the increment method, saves the updated wallet back to the backend, and updates the balance state. Bind the topUp function to the “top up” button using the click event handler.

Next, to the Home component, add a logOutUser function responsible for logging out the user. The function should look like this:

const logOutUser = async () => {
  try {
    const user = await Parse.User.logOut();
    console.log("User logged out successfully:", user);
    navigate("/");
  } catch (error) {
    console.error("Error logging out user:", error);
  }
};

The logOutUser function calls the logOut method of the Parse.User class asynchronously to log out the current user. Bind the logOutUser function to the “log out” button in the Home component using the click event.

To style the Home component, add the CSS classes defined below to the index.css file.

.home{
  display: flex;
  flex-direction: column;
  gap: 3rem;
  margin-block-start: 4rem;
}

.title{
  text-align: center;
  margin-block-start: 2rem;
}

.top{
  font-size: 25px;
  font-weight: 500;
}

.balance-card{
  background-color: #FFFFFF;
  inline-size: 40%;
  padding: 1rem 2rem;
  border-radius: 7px;
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.features{
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 2rem;
}

.card{
  background-color: #FFFFFF;
  color: #00C3A5;
  margin-block-end: 1rem;
  padding: 1rem 2rem;
  font-size: 25px;
  border-radius: 10px;
}

.card:hover{
  opacity: 0.6;
}

Testing your Application

Once you are done building the React application and implementing the user authentication with Back4app.

The next step is to test the application. To do this, navigate to the link http://localhost:5173/ on your web browser, and the sign-up page will display on your screen.

Fill out the sign-up form and submit it.

Sign Up On Banka

After signing up, you can confirm if the sign-up was successful by visiting your application dashboard on Back4app. If the sign-up is successful, Back4app will add the user details to the User class in your application.

Like so:

After signing up, the application will redirect you to the login page, where you will log in using the username and password you used in the sign-up form.

For example:

Login to Banka

Once you are logged in, the home page will display on your screen, and it should look like this:

Banka Dashboard

Now click on the “top up” button to increase your balance by 10.

Top Up Banka

Deploying Your React Application on Back4app

To deploy your React application on Back4app, you will use Back4app’s Container as a Service (CaaS) application.

Back4app’s CaaS allows you to deploy and manage Docker containers in the cloud environment provided by Back4App.

Before you can deploy your React application, you first need to dockerize the application. To do this, create a Dockerfile in the root directory of your application and add the following lines of code:

FROM node:16
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5173
CMD ["npm", "run", "dev"]

This Dockerfile sets up a Node.js environment, creates a working directory, installs dependencies, copies the application code, exposes a port, and specifies the default command to run the application.

Also, create a .dockerignore file. In the .dockerignore file, add the files and directories excluded from the context when building Docker images.

For example:

#.dockerignore
node_modules

Since you created the React application with Vite, you must update the Vite config file to support Docker. In your vite.config.js file, replace the existing code with the code block below:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// <https://vitejs.dev/config/>
export default defineConfig({
  plugins: [react()],
  server: {
    host: true,
    strictPort: true,
    port: 5173,
   },
})

This code block sets the server to use the default host, usually localhost. It sets the port number that the development server will listen on to port 5173 and ensures that Vite will fail to start the server if the specified port is unavailable.

You can now build your application’s docker image by running the following command in your terminal:

docker build -t quote-generator .

Ensure that you push your application to your GitHub account before attempting to deploy the application on Back4app. Once you are done pushing the application, integrate your GitHub account with Back4app.

You can do this with the Back4app Github app. After integrating your GitHub with Back4app, you can now deploy your application using the Back4app AI Agent.

To deploy the application, enter the prompt below:

Deploy my repository <<repository-url>> on Back4app containers

In the code block above, replace the repository-url with your application’s GitHub repository URL.

Deploy Application on Back4app Containers

As shown in the image above, when communicating with the AI agent, provide the GitHub repository link of the application you wish to deploy.

After Back4app’s AI Agent initiates the application’s deployment, wait a couple of minutes, then confirm the current status of the deployment.

For example:

App Live URL

Upon successful deployment, the Back4app AI Agent will update the deployment status of the application to “deployed” and provide you with a link to access the application on your browser.

You can find the finished app on this GitHub repository and the live app on this URL.

Conclusion

This article explained how to add authentication to a React app using a backend as a service.

Integrating user authentication into your React app using Back4App is a straightforward process that significantly enhances the security and functionality of your application.

By leveraging Back4App’s services, you can focus on building your web applications with great user experiences while leaving the backend management and authentication tasks to a reliable platform.


Leave a reply

Your email address will not be published.