Integration of Facial Authentication (Recognition) on an Employee Dashboard using FACEIO, Next.js & Typescript.

envitab

Ekemini Samuel

Posted on May 13, 2024

Integration of Facial Authentication (Recognition) on an Employee Dashboard using FACEIO, Next.js & Typescript.

While there are different authentication methods, Facial Authentication adds a higher level of security. In this tutorial, we'll integrate Facial Authentication with FACEIO to an employee dashboard sign-up page.

At the end of this project, you can sign in to the Employee dashboard and be authenticated with FACEIO Authentication.

FACEIO GIF

Let's begin!

Prerequisites

What is FACEIO?

FACEIO is a facial authentication framework that can be implemented on any website with JavaScript to easily authenticate users via Face Recognition instead of using a login/password or OTP code.

Creating the UI for the Employee Dashboard Signup/Login Page

Step 1: Create a Next.JS app for the project
Open your terminal, navigate to your workspace folder, and run the command below:



npx create-next-app@latest employee-next-app


Enter fullscreen mode Exit fullscreen mode

This will create an employee-next-app directory with a Next.JS project structure. Select Yes for all the prompts (except the last one, as the default import alias is recommended) to install Typescript, TailwindCSS, and other dependencies needed for the Next.js project.

nextjs terminal

Still in your terminal, navigate to the project directory like so:



cd employee-next-app


Enter fullscreen mode Exit fullscreen mode

Step 2: Install the FACEIO library for facial authentication. Read the docs to learn more.
In the employee-next-app directory, run this command to install fio.js



npm i @faceio/fiojs


Enter fullscreen mode Exit fullscreen mode

fio js

After installing the fio.js library, run this command to open the project in your VS Code editor:



code .


Enter fullscreen mode Exit fullscreen mode

In the src\app directory, create a file named faceio_fiojs.d.ts and enter the code below:



declare module "@faceio/fiojs"


Enter fullscreen mode Exit fullscreen mode

module

This initializes a module to interact with the fio.js library.

Step 3: Creating the Reusable Components
Next, we'll create components that will be used for the employee dashboard.
Create a Component folder in the src\app directory. Then create a new file Sidebar.tsx and enter the code below, this will create a Sidebar component.



import React from "react";

const Sidebar = () => {
  return (
    <nav className="space-y-2">
      <a
        className="flex items-center gap-2 rounded-md px-3 py-2 hover:bg-gray-200"
        href="#"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="24"
          height="24"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
          className="h-5 w-5 text-black"
        >
          <rect width="7" height="9" x="3" y="3" rx="1"></rect>
          <rect width="7" height="5" x="14" y="3" rx="1"></rect>
          <rect width="7" height="9" x="14" y="12" rx="1"></rect>
          <rect width="7" height="5" x="3" y="16" rx="1"></rect>
        </svg>
        <span className="text-black">Dashboard</span>
      </a>
      <a
        className="flex items-center gap-2 rounded-md px-3 py-2 hover:bg-gray-200"
        href="#"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="24"
          height="24"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
          className="h-5 w-5 text-black"
        >
          <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path>
          <circle cx="9" cy="7" r="4"></circle>
          <path d="M22 21v-2a4 4 0 0 0-3-3.87"></path>
          <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
        </svg>
        <span className="text-black">Team</span>
      </a>
      <a
        className="flex items-center gap-2 rounded-md px-3 py-2 hover:bg-gray-200"
        href="#"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="24"
          height="24"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
          className="h-5 w-5 text-black"
        >
          <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"></path>
          <path d="M14 2v4a2 2 0 0 0 2 2h4"></path>
          <path d="M10 9H8"></path>
          <path d="M16 13H8"></path>
          <path d="M16 17H8"></path>
        </svg>
        <span className="text-black">HR</span>
      </a>
      <a
        className="flex items-center gap-2 rounded-md px-3 py-2 hover:bg-gray-200"
        href="#"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="24"
          height="24"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
          className="h-5 w-5 text-black"
        >
          <path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"></path>
          <circle cx="12" cy="12" r="3"></circle>
        </svg>
        <span className="text-black">Settings</span>
      </a>
    </nav>
  );
};

export default Sidebar;


Enter fullscreen mode Exit fullscreen mode

Also, create the Navigation bar component with a file called Navbar.tsx and enter the code below, still in the Component directory:



import React from "react";

const Navbar = () => {
  return (
    <div>
      <header className="bg-gray-900 text-white">
        <div className="container mx-auto flex h-16 items-center justify-between px-4 md:px-6">
          <a className="flex items-center gap-2" href="#">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="24"
              height="24"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
              className="h-6 w-6"
            >
              <path d="m8 3 4 8 5-5 5 15H2L8 3z"></path>
            </svg>
            <span className="text-lg font-semibold">Speed Inc</span>
          </a>
          <nav className="hidden space-x-4 md:flex">
            <a className="hover:text-gray-300" href="#">
              Dashboard
            </a>
            <a className="hover:text-gray-300" href="#">
              Team
            </a>
            <a className="hover:text-gray-300" href="#">
              HR
            </a>
            <a className="hover:text-gray-300" href="#">
              Settings
            </a>
          </nav>
          <button className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 w-10 md:hidden">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="24"
              height="24"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
              className="h-6 w-6"
            >
              <line x1="4" x2="20" y1="12" y2="12"></line>
              <line x1="4" x2="20" y1="6" y2="6"></line>
              <line x1="4" x2="20" y1="18" y2="18"></line>
            </svg>
            <span className="sr-only">Toggle navigation</span>
          </button>
        </div>
      </header>
    </div>
  );
};

export default Navbar;


Enter fullscreen mode Exit fullscreen mode

Step 4: Creating the Dashboard page
Next, let's create a Dashboard page that users who are authenticated with FACEIO can access. We will import the Navigation bar and Sidebar components we created above using:



import Navbar from "../Component/Navbar";
import Sidebar from "../Component/Sidebar";


Enter fullscreen mode Exit fullscreen mode

Inside the src/app directory, create a new folder named dashboard and a file named Page.tsx. Enter the following code into Page.tsx:



"use client";
import React, { useEffect } from "react";
import { useRouter } from "next/navigation";
import Navbar from "../Component/Navbar";
import Sidebar from "../Component/Sidebar";

const Page = () => {
  const router = useRouter();

  useEffect(() => {
    const faceid = localStorage.getItem("faceId");
    if (!faceid) {
      router.push("/");
    }
  }, [history]);

    // Add JSON DATA here

  return (
    <>
      <Navbar />
      <div className="flex min-h-screen flex-col">
        <div className="flex flex-1">
          <div className="hidden w-64 bg-gray-100 p-4 md:block">
            <Sidebar />
          </div>
          <main className="flex-1 bg-gray-50 p-4 md:p-6">
            {/* Dashboard Section */}
            <section>
              {/* Dashboard content */}
              <div className="flex items-center justify-between">
                <h2 className="text-2xl font-bold">Dashboard</h2>
                <button className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2">
                  View All
                </button>
              </div>
              <div className="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
                {dashboardData.map((item, index) => (
                  <div
                    key={index}
                    className="rounded-lg border bg-card text-card-foreground shadow-sm"
                    data-v0-t="card"
                  >
                    <div className="flex flex-col space-y-1.5 p-6">
                      {item.icon}
                    </div>
                    <div className="p-6">
                      <div className="text-4xl font-bold">{item.value}</div>
                      <div className="text-gray-500">{item.label}</div>
                    </div>
                  </div>
                ))}
              </div>
            </section>
            {/* Team Directory Section */}
            <section>
              {/* Team directory content */}
              <div className="flex items-center justify-between mt-4">
                <h2 className="text-2xl font-bold">Team Directory</h2>
                <button className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2">
                  View All
                </button>
              </div>
              <div className="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
                {teamDirectoryData.map((item, index) => (
                  <div
                    key={index}
                    className="rounded-lg border bg-card text-card-foreground shadow-sm"
                    data-v0-t="card"
                  >
                    <div className="flex flex-col space-y-1.5 p-6">
                      <img
                        src={item.image}
                        width="48"
                        height="48"
                        className="rounded-full"
                      />
                    </div>
                    <div className="p-6">
                      <div className="font-semibold">{item.name}</div>
                      <div className="text-gray-500">{item.role}</div>
                      <div className="text-gray-500">{item.email}</div>
                    </div>
                  </div>
                ))}
              </div>
            </section>
            {/* HR Resources Section */}
            <section>
              {/* HR resources content */}
              <div className="flex items-center justify-between mt-4">
                <h2 className="text-2xl font-bold">HR Resources</h2>
                <button className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2">
                  View All
                </button>
              </div>
              <div className="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
                {hrResourcesData.map((item, index) => (
                  <div
                    key={index}
                    className="rounded-lg border bg-card text-card-foreground shadow-sm"
                    data-v0-t="card"
                  >
                    <div className="flex flex-col space-y-1.5 p-6">
                      {item.icon}
                    </div>
                    <div className="p-6">
                      <div className="font-semibold">{item.title}</div>
                      <div className="text-gray-500">{item.description}</div>
                    </div>
                    <div className="flex items-center p-6">
                      <a className="text-blue-500 hover:underline" href="#">
                        Read More
                      </a>
                    </div>
                  </div>
                ))}
              </div>
            </section>
          </main>
        </div>
      </div>
    </>
  );
};

export default Page;


Enter fullscreen mode Exit fullscreen mode

FACEIO provides a unique face ID for every user that is authenticated and registers on the Signup page (we will create this page in the steps below)

When a user tries to log in, the useEffect hook in the code above is used to check if the faceId of that user is stored in localStorage and ensure that only authenticated users (with a stored faceId) can access the Dashboard.

If not, the user will be redirected to the login page with router.push("/");.

You can access the JSON data here, which is used to populate the Dashboard with employee information. Add it into the Dashboard component code above after the useEffect hook, there is a // Add JSON DATA here comment for clarity. πŸ˜‰

Step 5: Creating the Signup Page
Now, we will create the Signup page where users either register or log in and will be authenticated using FACEIO before accessing the employee dashboard page.

Navigate to the Component directory we created in Step 3, and create a SignupForm.tsx file. Then enter the code below in SignupForm.tsx:



"use client";
import React, { useEffect, useState } from "react";
import faceIO from "@faceio/fiojs";
import { useRouter } from "next/navigation";
import Navbar from "./Navbar";
import Sidebar from "./Sidebar";


const SignUpForm: React.FC = () => {
  const router = useRouter();
  const [payload, setPayload] = useState<Payload>({ userEmail: "", pin: "" });
  const [isSigningUp, setIsSigningUp] = useState<boolean>(false); // New state variable

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setPayload({ ...payload, [e.target.name]: e.target.value });
  };


  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
  };

  const toggleSignUp = () => {
    setIsSigningUp((prevState) => !prevState);
  };

  return (
    <>
      <Navbar />
      <div className="flex min-h-screen flex-col">
        <div className="flex flex-1">
          <div className="hidden w-64 bg-gray-100 p-4 md:block">
            <Sidebar />
          </div>
          <main className="flex-1 bg-gray-50 p-4 md:p-6">
            <div className="grid gap-6">
              <section className="flex items-center justify-center">
                <div
                  className="rounded-lg border bg-card text-card-foreground shadow-sm w-full max-w-md"
                  data-v0-t="card"
                >
                  <form onSubmit={handleSubmit}>
                    <div className="flex flex-col space-y-1.5 p-6">
                      <h3 className="text-left whitespace-nowrap font-black tracking-tight text-2xl text-black">
                        {isSigningUp ? "Sign Up" : "Login"}
                      </h3>
                      <p className="text-sm text-muted-foreground text-gray-600 text-left">
                        {isSigningUp
                          ? "Create your account by entering your email and password."
                          : "Enter your email and password to access your account."}
                      </p>
                    </div>
                    <div className="p-6 space-y-4 text-left">
                      <div className="space-y-2">
                        <label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-black text-left">
                          Email
                        </label>
                        <input
                          className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 text-black"
                          id="email"
                          placeholder="m@example.com"
                          type="email"
                          name="userEmail"
                          value={payload.userEmail}
                          onChange={onChange}
                        />
                      </div>
                      <div className="space-y-2">
                        <label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-black text-left">
                          Password
                        </label>
                        <input
                          className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 text-black"
                          id="password"
                          type="password"
                          name="pin"
                          value={payload.pin}
                          onChange={onChange}
                        />
                      </div>
                    </div>
                    <div className="flex items-center p-6">
                      <button className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2 w-full bg-gray-900">
                        {isSigningUp ? "Sign Up" : "Login"}
                      </button>
                    </div>
                  </form>
                </div>
              </section>
              <div className="flex justify-center">
                <button
                  className="text-blue-600 underline mt-4"
                  onClick={toggleSignUp}
                >
                  {isSigningUp ? "Login instead" : "Sign Up instead"}
                </button>
              </div>
            </div>
          </main>
        </div>
      </div>
    </>
  );
};

export default SignUpForm;


Enter fullscreen mode Exit fullscreen mode

In the code above; import faceIO from "@faceio/fiojs";: is used to import the faceIO module from the @faceio/fiojs library, which provides the functionality for facial recognition and authentication using FACEIO. It allows us to methods like enrollUser to register new users' facial data with FACEIO or authenticateUser to verify a user's identity through facial recognition.

useState hook manages the Signup component's state, while the payload state holds the user's email (userEmail) and PIN (pin) for signup or login. Initially, these values are set to empty strings ("").

The toggleSignUp function allows us to switch between signup and login modes within the form. It toggles the isSigningUp state from false to true or vice versa. When the page is loaded it is set by default to false, representing the login mode.

Navigate to the src\app directory where you'll find the page.tsx file that is created by default when setting up a Next.js project. Open the file and enter the following code:



import Image from "next/image";
import Head from "next/head";
import SignUpForm from "./Component/SignupForm";

export default function Home() {
  return (
    <div className="bg-green-300 text-center text-white h-screen">
      <Head>
        <title>face.IO login/signup form</title>
        <meta name="description" content="a face.IO login form" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main>
        <SignUpForm />
      </main>
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

In the code above, we import the Signup form component using: import SignUpForm from "./Component/SignupForm";, which will be rendered the when web application is loaded on the browser.

Step 6: Integrating FACEIO to the Next.js Web App
At this point, we have created the Sidebar, Navbar, Dashboard, and Signup/Login page.

Next, we will integrate FACEIO to authenticate users when they Signup/Login, before accessing the Dashboard.

Create a FACEIO account and sign in to your FACEIO Console to get the credentials needed to add Facial authentication.

FaceIO Console

On the FACEIO dashboard, click on New Application to create a FACEIO application, and follow the steps in the application wizard.

When asked to select a Facial Recognition Engine for the application, choose PixLab Insight. The engine will be used for mapping each enrolled user’s face in real-time into a mathematical feature vector, known as a biometric hash, which is in turn stored in a sand-boxed binary index. This is used to protect the data of users when they are authenticated with FACEIO.

facio app

Your new FACEIO app will have an ID number, take note of it as we'll be using it to integrate FACEIO into the web application.

FACEIO appID

Go to the SignupForm.tsx file and declare a variable; faceio that will initialise the FACEIO SDK with your specific FACEIO application ID. The FACEIO app ID will be retrieved from an environment configuration env file.

In the root directory of your Next.js project, create a new file named .env.local and enter this to define the environment variable:



NEXT_PUBLIC_FACE_IO_ID=your_faceio_app_id_here


Enter fullscreen mode Exit fullscreen mode

Replace "your_faceio_app_id_here" with your actual FACEIO application ID, and add .env.local to your .gitignore file

Then continue with this code in the SignupForm.tsx file



const SignUpForm: React.FC = () => {
  const router = useRouter();

    // Retrieve FACEIO application ID from environment variable

  const faceioID = process.env.NEXT_PUBLIC_FACE_IO_ID;

  const faceio = new faceIO(faceioID);

  const [payload, setPayload] = useState<Payload>({ userEmail: "", pin: "" });
  const [isSigningUp, setIsSigningUp] = useState<boolean>(false); // New state variable

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setPayload({ ...payload, [e.target.name]: e.target.value });
  };


Enter fullscreen mode Exit fullscreen mode

Then define the enrollNewUser function responsible for enrolling new users through FACEIO's facial recognition and authentication service like so:



const enrollNewUser = async () => {
    try {
      const userInfo = await faceio.enroll({
        locale: "auto",
        payload: {
          email: payload.userEmail,
          pin: payload.pin,
        },
      });
      alert(
        `User Successfully Enrolled! Details:
        Unique Facial ID: ${userInfo.facialId}
        Enrollment Date: ${userInfo.timestamp}
        Gender: ${userInfo.details.gender}
        Age Approximation: ${userInfo.details.age}`
      );
      localStorage.setItem("faceId", userInfo.facialId);
      router.push("/dashboard");

      console.log(userInfo);
    } catch (error) {
      console.error("Enrollment failed:", error);
    }
  };


Enter fullscreen mode Exit fullscreen mode

This function registers a new user by default, gets the user's email and user pin as payload data and asynchronously calls the inbuilt faceio.enroll() function from FACEIO, read more about it in the documentation.

When registration is successful, you'll see an alert alert() message box with these details generated by FACEIO:

  • FaceID
  • Enrollment date
  • Gender
  • Approximated age

It stores the user's facial ID userInfo.facialId) in the browser's local storage, hence the localStorage.setItem() in the code above.

router.push() redirects the user to the dashboard page (/dashboard) after successful enrollment.

If the facial authentication is not successful, there is a catch(error) function that gets the error and console.logs the error for debugging.

Now let's move on to the Login functionality by creating an authenticateUser function, still in the SignupForm.tsx file:



const authenticateUser = async () => {
    try {
      const userData = await faceio.authenticate({
        locale: "auto",
      });
      console.log("Success, user identified");
      console.log("Linked facial Id: " + userData.facialId);
      console.log("Payload: " + JSON.stringify(userData.payload));
      localStorage.setItem("faceId", userData.facialId); // Store face ID in local storage
      router.push("/dashboard");
    } catch (error) {
      console.error("Authentication failed:", error);
    }
  };


Enter fullscreen mode Exit fullscreen mode

faceio.authenticate is another built-in function from FACEIO which checks if there is an existing user with the face that is scanned and the pin/email entered.

It then console.logs: Success, User Identified, and stores the UserID in the browser local storage like so: localStorage.setItem("faceId", userData.facialId); and redirects to the Dashboard page using:router.push("/dashboard");

Note that the SignUp and Login page UI are both written in the SignupForm.tsx code, however, their functionalities differ.

We use a boolean to set the state of the Signup form:
const [isSigningUp, setIsSigningUp] = useState<boolean>(false);

By default, it is set to false, but when you click on theSign Up instead button it sets the state to true, which then displays the sign-up form, interchanging the values per state.

That's where the toggleSignUp function comes in like so:



const toggleSignUp = () => {
    setIsSigningUp((prevState) => !prevState);
  };


Enter fullscreen mode Exit fullscreen mode

The handleSubmit function checks if isSigningUp
is true, if yes it calls the enrollNewUser() function, else it calls the authenticateUser() function like so:



 const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    console.log(payload);
    if (isSigningUp) {
      enrollNewUser();
    } else {
      authenticateUser();
    }
  };


Enter fullscreen mode Exit fullscreen mode

Step 7: Running the application
Start the development server by running:



npm run dev


Enter fullscreen mode Exit fullscreen mode

The web app can be accessed in your browser with this URL

http://localhost:3000

When you open the URL in your browser, the Signup/Login page loads like so:

Sign Up Login

As a new user, click on Sign Up instead, then enter your email and password to begin the authentication process with FACEIO.

prompt

Then accept the terms & conditions, allow access to your camera, and get authenticated:

Accept

You will also be prompted to set a PIN code of at least 4 digits and no more than 16 digits

auth

When the authentication is successful, you get this displayed on your browser - FACEIO automatically generates a description of the user that has been authenticated.

details

Now you can Login with your email & password, complete the authentication, enter your PIN and access the employee dashboard 😎

Dashboard

And that's it! We have successfully integrated facial authentication using FACEIO into an employee dashboard web application.

To clone the project and run it locally, open your terminal and run this command:



git clone https://github.com/Tabintel/employee-next-app


Enter fullscreen mode Exit fullscreen mode

Then run npm install to install all dependencies needed for the project and npm run dev to run the web app.

Get the full source code on GitHub.

To get started with FACEIO, sign up here and begin adding Facial authentication to your websites/web application, providing your users with more security.

Learn more about how to use FACEIO in the documentation.

πŸ’– πŸ’ͺ πŸ™… 🚩
envitab
Ekemini Samuel

Posted on May 13, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related