Designing a React E-Commerce App for Digital Products—Part 2 (Handling Authentication and Authorization)
Tags: react, webdev, ecommerce, digital
Published: 2023-12-28 07:13:11

---
title: Designing a React E-Commerce App for Digital Products—Part 2 (Handling Authentication and Authorization)
published: true
date: 2023-12-07 11:19:00 UTC
tags: react, webdev, ecommerce, digital
canonical_url: https://www.syncfusion.com/blogs/post/react-ecommerce-app-part-2.aspx
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apah0rodtbv1fr3zavod.png
---

In [part 1](https://www.syncfusion.com/blogs/post/react-digital-ecommerce-app.aspx "Blog: Designing a React E-Commerce App for Digital Products—Part 1") of this series, we created an e-commerce web application in React to sell digital products. We created an architectural diagram to get an idea about the user navigation and wireframes and set up the boilerplate with the public and private routes.

In this part, we will handle the authentication and authorization of the web app. We’ll create the signup and login pages and integrate them with a database to persist the data.

Let’s get started!

## Using SupaBase as the database

In this demo, we’ll use [SupaBase](https://supabase.com/ "SupaBase Web page"), which is an alternative to Firebase and provides an easy-to-use and integrated database (DB).

First, we sign into SupaBase, create a new project, and then get our project URL and API keys to create an instance of the DB.
<figure>

  <img src="https://www.syncfusion.com/blogs/wp-content/uploads/2023/12/Supabase-API-settings-reference.png" alt="Supabase API settings reference" style="width:100%">

  <figcaption>Supabase API settings reference</figcaption>

</figure>

Let’s use its JavaScript SDK to use the database directly from the React code.

```
npm install @supabase/supabase-js
```

To use the SupaBase, we have to create an instance of it so that we can invoke different methods of it. What I did was create a hook for the SupaBase to instantiate and then use it in all other hooks.

```js
// hooks/supabase.js
import { createClient } from "@supabase/supabase-js";

const useSupbase = () => {
  const supabase = createClient(
    "your-project-url",
    "your-anon-public-key"
  );

  return supabase;
};

export default useSupbase;
```

## Creating the useAuth() hook for authentication

The first important hook we are going to create is an authentication hook called **useAuth** () that lets users sign up and log into the app. SupaBase already provides methods for seamless signup and login actions, so we are going to make use of them.

For signup, we’ll store the email address, password, first name, and last name details. You can store as many additional details as you want.

For the login, we’ll accept the email address and password and verify if the credential is valid or not.

Refer to the following code example.

```js
// hooks/auth.js
import useSupbase from "“./supbase"”;
import useStore from "“../store/user"”;
import { useState } from "“react"”;

const useAuth = () => {
 const [signupData, setSignupData] = useState(null);
 const [loginData, setLoginData] = useState(null);
 const [error, setError] = useState(null);

 const supabase = useSupbase();
 const setUserState = useStore((state) => state.setUserState);

 const signup = async (email, password, firstName, lastName) => {
   try {
     const { data, error } = await supabase.auth.signUp({
       email: email,
       password: password,
       options: {
         data: {
           firstName,
           lastName,
         },
       },
     });
     if (!error) {
       setSignupData(data);
     } else {
       setError(error);
     }
   } catch € {
     console.error("Error while creating an user please try agaiin!");
     setError€(e);
   }
 };

 const login = async (email, password) => {
   try {
     const { data, error } = await supabase.auth.signInWithPassword({
       email,
       password,
     });

     if (!error) {
       const { session, user } = data;
       setLoginData(data);
       setUserState({
         isLoggedIn: true,
         email: user.email,
         firstName: user.user_metadata.firstName,
         lastName: user.user_metadata.lastName,
         accessToken: session.access_token,
       });
     } else {
       setError(error);
     }
   } catch (error) {
     console.error("Invalid credenntials", error);
     setError(error);
   }
 };

 return { login, loginData, signup, signupData, error };
};

export default useAuth;
```

The **useAuth()** hook provides the methods for signup and login and their respective data and errors, which helps us to abstract similar logic to a single stage.

Once the user has successfully logged in, we’ll store the user details like first name and last name in the [**Zustand**](https://www.npmjs.com/package/zustand "zustand NPM Package") along with the bearer access token. We’ll use them for further authorization to get the user-specific data from the database.

For this, the user state has to be updated in the Zustand to accommodate additional details.

```js
import { create } from "zustand";
import { persist } from "zustand/middleware";

let store = (set) => ({
 isLoggedIn: false,
 email: "",
 firstName: "",
 lastName: "",
 accessToken: "",
 setUserState: ({ isLoggedIn, email, firstName, lastName, accessToken }) =>
   set(() => ({ isLoggedIn, email, firstName, lastName, accessToken })),
});

//persist the state with key "randomKey"
store = persist(store, { name: "user-store" });

//create the store
let useStore = create(store);

export default useStore;
```

On successful login, we’ll also change the state **isLoggedIn** to **true**. That will change the router to show on the dashboard page that the user is logged in.

## Creating the signup and login pages

By default, the user will be moved to the login page, and there will be a link at the bottom for the user to sign up if they haven’t already.

Let’s start by creating the signup page. Add it to the router and then create the page.

```js
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Login from "./pages/login";
import Signup from "./pages/signup";
import PrivateRoutes from "./routes/private";
import PublicRoutes from "./routes/public";
import NotFound from "./pages/404";

const App = () => {
 return (
   <BrowserRouter>
    <Routes>
     <Route path="/" element={<PublicRoutes />}>
      <Route index element={<h1>Browse</h1>} />
      <Route path="product-list" element={<h1>Product List</h1>}></Route>
      <Route path="login" element={<Login />}></Route>
      <Route path="signup" element={<Signup />}></Route>
     </Route>
     <Route path="/" element={<PrivateRoutes />}>
      <Route path="dashboard" element={<h1>Dashboard</h1>}></Route>
      <Route path="product-add" element={<h1>Product Add</h1>}></Route>
      <Route path="checkout" element={<h1>checkout</h1>}></Route>
      <Route path="thank-you" element={<h1>Thank You</h1>}></Route>
     </Route>
     <Route path="*" element={<NotFound />} />
    </Routes>
   </BrowserRouter>
 );
};

export default App;
```

### Signup page

The signup page is part of the public routes, as users can access it without being logged in. To sign up, users must provide their first name, last name, email address, and password, and then re-enter the password to verify.
<figure>

  <img src="https://www.syncfusion.com/blogs/wp-content/uploads/2023/12/Signup-page-in-React-e-commerce-app.png" alt="Signup page in React e-commerce app" style="width:100%">

  <figcaption>Signup page in React e-commerce app</figcaption>

</figure>

To create the layout, we’ll use the [Syncfusion React inputs, buttons, and card controls](https://www.syncfusion.com/react-components "React UI Components Library").

```js
import "@syncfusion/ej2-layouts/styles/material.css";
import "@syncfusion/ej2-react-inputs/styles/material.css";
import "@syncfusion/ej2-react-buttons/styles/material.css";
import "./login.css";
import { ButtonComponent } from "@syncfusion/ej2-react-buttons";
import { useState, useCallback, useEffect } from "react";
import { debounce } from "lodash";
import Toast from "../components/Toast";
import { Link, useNavigate } from "react-router-dom";
import useAuth from "../hooks/auth";

const Signup = () => {
 const navigate = useNavigate();
 const [email, setEmail] = useState("");
 const [password, setPassword] = useState("");
 const [repassword, setRepassword] = useState("");
 const [firstName, setFirstName] = useState("");
 const [lastName, setLastName] = useState("");
 const [invalidEmail, setInvalidEmail] = useState(false);
 const [passwordMatching, setPasswordMatching] = useState(true);
 const [toast, setToast] = useState({
   show: false,
   message: "",
   type: "error",
 });
 const { signup, signupData } = useAuth();

 // helper function that checks email using regex
 const isValidEmail = (email) => {
   return /\S+@\S+\.\S+/.test(email);
 };

 // function to validate email
 const validateEmail = (text) => {
   if (!!text && !isValidEmail(text)) {
     setInvalidEmail(true);
   } else {
     setInvalidEmail(false);
   }
 };

 // debounce the function to initiate test
 // when user stops writing for certain time
 const debouncedValidateEmail = useCallback(debounce(validateEmail, 1000), []);

 const handleEmailChange = (e) => {
   setEmail(e.target.value);
   debouncedValidateEmail(e.target.value);
 };

 // monitor change of re-entered password
 useEffect(() => {
   // if both the passwords mismatch, update the state to show error
   if (password && repassword && password !== repassword) {
     setPasswordMatching(false);
   } else {
     setPasswordMatching(true);
   }
 }, [repassword]);

 // on signup
 const handleSubmit = () => {
   // if the required fields are empty
   if (!firstName || !lastName || !email || !password) {
     // show toast message
     setToast({
       message: "Required fields are missing",
       show: true,
       type: "error",
     });
   } else {
     // initiate signup
     signup(email, password, firstName, lastName);
   }
 };

 // monitor the change in data after signup
 useEffect(() => {
   // if user is successfully authenticated
   if (signupData?.user?.role === "authenticated") {
     // show toast message
     setToast({
       message: "Successfully signed up",
       show: true,
       type: "success",
     });

     // and redirect to the login page
     setTimeout(() => {
       navigate("/login");
     }, 2000);
   }
 }, [signupData]);

 return (
   <div className="e-card login-container">
     <h1 className="text-center">Welcome to Geva Digital Shop</h1>
     <h2 className="text-center">Signup to sell any digital product</h2>
     <div className="field-area">
       <label htmlFor="first-name">First Name *</label>
       <input
         className="e-input"
         type="text"
         placeholder="Your first name..."
         name="first-name"
         id="first-name"
         onChange={(e) => setFirstName(e.target.value)}
         value={firstName}
         required
       />
     </div>
     <div className="field-area">
       <label htmlFor="last-name">Last Name *</label>
       <input
         className="e-input"
         type="text"
         placeholder="Your last name..."
         name="last-name"
         id="last-name"
         onChange={(e) => setLastName(e.target.value)}
         value={lastName}
         required
       />
     </div>
     <div className="field-area">
       <label htmlFor="email">Email *</label>
       <input
         className="e-input"
         type="email"
         placeholder="Your email..."
         name="email"
         id="email"
         onChange={handleEmailChange}
         value={email}
         required
       />
       {invalidEmail && (
         <p className="error">Please enter a valid email address</p>
       )}
     </div>
     <div className="field-area">
       <label htmlFor="password">Password *</label>
       <input
         className="e-input"
         type="password"
         placeholder="Your password..."
         name="password"
         id="password"
         onChange={(e) => setPassword(e.target.value)}
         value={password}
         required
       />
     </div>
     <div className="field-area">
       <label htmlFor="re-password">Re-enter Password *</label>
       <input
         className="e-input"
         type="password"
         placeholder="Re-enter your password..."
         name="re-password"
         id="re-password"
         onChange={(e) => setRepassword(e.target.value)}
         value={repassword}
         required
         disabled={!password}
       />
     </div>
     {repassword && !passwordMatching && (
       <span className="text-center" style={{ color: "red" }}>
         Entered passwords does not match
       </span>
     )}
     <div
       style={{
         width: "120px",
         margin: "20px auto 0 auto",
       }}
     >
       <ButtonComponent
         cssClass="e-success e-block"
         type="submit"
         onClick={handleSubmit}
         style={{ fontSize: "1.2em" }}
       >
         Signup
       </ButtonComponent>
     </div>
     {toast.show && (
       <Toast
         errorMessage={toast.message}
         type={toast.type}
         onClose={() => {
           setToast({
             show: false,
             message: "",
             type: "error",
           });
         }}
       />
     )}
     <span className="text-center" style={{ marginTop: "1em" }}>
       Already have an account? <Link to="/login">login</Link>
     </span>
   </div>
 );
};

export default Signup;
```

Now, we’ve created the controlled components and maintained the state for each input.

On the email change, we’ll validate that the entered email address is in the correct format using [Regex](https://en.wikipedia.org/wiki/Regular_expression "Wikipedia Link: Regular expression") and [debouncing](https://learnersbucket.com/examples/interview/usedebounce-hook-in-react/ "useDebounce() hook in React") the input using the **lodash.debounce**.

Also, on submission, we are validating whether all the inputs are filled or not. If not, we’ll show an error with the help of Synfusion’s [React Toast](https://www.syncfusion.com/react-components/react-toast "React Toast Notifications") component.

```js
Import { ToastComponent } from “@syncfusion/ej2-react-notifications”;
import “@syncfusion/ej2-base/styles/material.css”;
import “@syncfusion/ej2-react-buttons/styles/material.css”;
import “@syncfusion/ej2-react-popups/styles/material.css”;
import “@syncfusion/ej2-react-notifications/styles/material.css”;
const TOAST_TYPES = {
 warning: “e-toast-warning”,
 success: “e-toast-success”,
 error: “e-toast-danger”,
 info: “e-toast-infor”,
};

const Toast = ({ errorMessage, type, onClose }) => {
 let toastInstance;

 let position = { X: “Center” };

 function toastCreated() {
   toastInstance.show();
 }

 function toastDestroyed€ {
   e.clickToClose = true;
   onClose && onClose();
 }

 return (
   <div>
     <ToastComponent
       ref={(toast) => (toastInstance = toast)}
       title={type.toUpperCase()}
       content={errorMessage}
       position={position}
       created={toastCreated.bind(this)}
       click={toastDestroyed.bind(this)}
       showCloseButton
       cssClass={TOAST_TYPES[type] || TOAST_TYPES["info"]}
     />
   </div>
 );
};
export default Toast;
```

If every input is filled, we sign up the user using the **signup** method from the **useAuth** () hook and show a success toast. Then, we navigate the user to the login page.

### Login page

To log in, we accept the email address and password and pass them to Supabase’s [signinwithpassword](https://supabase.com/docs/reference/javascript/auth-signinwithpassword "signinwithpassword method") method with our useAuth() hook.

Refer to the following image.
<figure>

  <img src="https://www.syncfusion.com/blogs/wp-content/uploads/2023/12/Login-page-in-React-e-commerce-app.png" alt="Login page in React e-commerce app" style="width:100%">

  <figcaption>Login page in React e-commerce app</figcaption>

</figure>

Refer to the following code example.

```js
import "@syncfusion/ej2-layouts/styles/material.css";
import "@syncfusion/ej2-react-inputs/styles/material.css";
import "@syncfusion/ej2-react-buttons/styles/material.css";
import "./login.css";
import { ButtonComponent } from "@syncfusion/ej2-react-buttons";
import { useState, useCallback, useEffect } from "react";
import { debounce } from "lodash";
import Toast from "../components/Toast";
import { Link } from "react-router-dom";
import useAuth from "../hooks/auth";

const Login = () => {
 const { login, loginData } = useAuth();
 const [email, setEmail] = useState("");
 const [password, setPassword] = useState("");
 const [invalidEmail, setInvalidEmail] = useState(false);
 const [showToast, setShowToast] = useState(false);

 // helper function that checks email using regex
 const isValidEmail = (email) => {
   return /\S+@\S+\.\S+/.test(email);
 };

 // function to validate email
 const validateEmail = (text) => {
   if (!!text && !isValidEmail(text)) {
     setInvalidEmail(true);
   } else {
     setInvalidEmail(false);
   }
 };

 // debounce the function to initiate test
 // when user stops writing for certain time
 const debouncedValidateEmail = useCallback(debounce(validateEmail, 1000), []);

 const handleEmailChange = (e) => {
   setEmail(e.target.value);
   debouncedValidateEmail(e.target.value);
 };

 const handleSubmit = () => {
   if (!email || !password) {
     setShowToast(true);
   } else {
     login(email, password);
   }
 };

 return (
   <div className="e-card login-container">
     <h1 className="text-center">Welcome to Geva Digital Shop</h1>
     <h2 className="text-center">Login to continue</h2>
     <div className="field-area">
       <label htmlFor="email">Email *</label>
       <input
         className="e-input"
         type="email"
         placeholder="Your email..."
         name="email"
         id="email"
         onChange={handleEmailChange}
         value={email}
         required
       />
       {invalidEmail && (
         <p className="error">Please enter a valid email address</p>
       )}
     </div>
     <div className="field-area">
       <label htmlFor="password">Password *</label>
       <input
         className="e-input"
         type="password"
         placeholder="Your password..."
         name="password"
         id="password"
         onChange={(e) => setPassword(e.target.value)}
         value={password}
         required
       />
     </div>
     <div
       style={{
         width: "120px",
         margin: "20px auto 0 auto",
       }}
     >
       <ButtonComponent
         cssClass="e-success e-block"
         type="submit"
         onClick={handleSubmit}
         style={{ fontSize: "1.2em" }}
       >
         Login
       </ButtonComponent>
     </div>
     {showToast && (
       <Toast
         errorMessage={"Please enter valid credentials"}
         type={"error"}
         onClose={() => {
           setShowToast(false);
         }}
       />
     )}
     <span className="text-center" style={{ marginTop: "1em" }}>
       Don't have an account? <Link to="/signup">signUp</Link>
     </span>
   </div>
 );
};

export default Login;
```

Here, too, we validate the email address and password. We show an appropriate error if the authentication fails using the Toast component.

If the user is successfully logged in, we’ll update the state in the Zustand in useAuth()’s login method. This will update the router and render the Dashboard page as the user is logged in.
<figure>

  <img src="https://www.syncfusion.com/blogs/wp-content/uploads/2023/12/Dashboard-page-in-React-e-commerce-app.png" alt="Dashboard page in React e-commerce app" style="width:100%">

  <figcaption>Dashboard page in React e-commerce app</figcaption>

</figure>

Now, the user can sign up and log into our e-commerce app!

## Conclusion

Thanks for reading! In this blog, we’ve seen how to design the Signup and Login pages for the React e-commerce app for digital products. We encourage you to put this knowledge into practice and share your experiences in the comments section below!

In the next part, we’ll design the home page with some dummy products to show and then the dashboard page, where the new products can be added.

[Syncfusion’s Essential Studio for React](https://www.syncfusion.com/react-ui-components "React UI Components Library") is a one-stop solution offering over 80 high-performance, lightweight, modular, and responsive UI components. It’s the perfect suite to build any web app.

For our existing Syncfusion users, the latest version of Essential Studio is readily available for download on the [License and Downloads](https://www.syncfusion.com/account/downloads "Essential Studio License and Downloads page") page. If you’re new to Syncfusion, we invite you to explore our products’ features and capabilities with our [30-day free trial](https://www.syncfusion.com/downloads "Get the free evaluation of Essential Studio products").

For questions, you can contact us through our [support forum](https://www.syncfusion.com/forums "Syncfusion Support Forum"), [support portal](https://support.syncfusion.com/ "Syncfusion Support Portal"), or [feedback portal](https://www.syncfusion.com/feedback "Syncfusion Feedback Portal"). We are always here to assist you!

## Related blogs

- [How to Build Synchronized Charts in React for Trade Analysis [Webinar Show Notes]](https://www.syncfusion.com/blogs/post/synchronized-charts-react.aspx "Blog: How to Build Synchronized Charts in React for Trade Analysis [Webinar Show Notes]")
- [Designing a React E-Commerce App for Digital Products—Part 1](https://www.syncfusion.com/blogs/post/react-digital-ecommerce-app.aspx "Blog: Designing a React E-Commerce App for Digital Products—Part 1")
- [Level Up Your React Charts: Mastering Scrollbar Customization](https://www.syncfusion.com/blogs/post/customize-react-charts-scrollbar.aspx "Blog: Level Up Your React Charts: Mastering Scrollbar Customization")
- [Top 7 Ways to Write CSS in Your React or Next.js App](https://www.syncfusion.com/blogs/post/write-css-react-next-js.aspx "Blog: Top 7 Ways to Write CSS in Your React or Next.js App")