Integration of Facial Authentication (Recognition) on an Employee Dashboard using FACEIO, Next.js & Typescript.
Tags: authentication, nextjs, webdev, javascript
Published: 2024-05-13 11:10:47

While there are different authentication methods, Facial Authentication adds a higher level of security. In this tutorial, we'll integrate Facial Authentication with [FACEIO](https://faceio.net/) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zx9t6p8jf5wnpteufoy7.gif)



Let's begin!

##Prerequisites
- Basic understanding of [Next.js](https://nextjs.org/) and [Typescript](https://www.typescriptlang.org/)
- A [FACEIO account](https://console.faceio.net/login)
- A [package manager ](https://www.npmjs.com/)installed (npm), and a [Code editor](https://code.visualstudio.com/) (VS Code or your favourite)

##What is FACEIO?
[FACEIO](https://faceio.net/getting-started) is a facial authentication framework that can be implemented on any website with [JavaScript](https://faceio.net/integration-guide) 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:

```shell
npx create-next-app@latest employee-next-app
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sda0eu7u73tb9c5lfyf9.png)



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

```shell
cd employee-next-app
```

**Step 2: Install the FACEIO library for facial authentication. Read the [docs](https://faceio.net/integration-guide) to learn more.**
In the `employee-next-app` directory, run this command to install `fio.js`

```shell
npm i @faceio/fiojs
```

![fio js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1qoh1n7c862eppdg1tc3.png)


After installing the `fio.js` library, run this command to open the project in your VS Code editor:
```shell
code .
```
In the `src\app` directory, create a file named `faceio_fiojs.d.ts` and enter the code below:

```jsx
declare module "@faceio/fiojs"
```

![module](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s52fdtrz2rl5fwvmadcc.png)



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.

```jsx
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;
```

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

```jsx
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;
```

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

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

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

```jsx
"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;
```

[FACEIO](https://faceio.net/dev-guides) 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](https://github.com/Tabintel/employee-next-app/blob/main/src/app/data.json), 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](https://faceio.net/) 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`:

```jsx
"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;
```

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](https://faceio.net/dev-guides). 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:

```jsx
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>
  );
}
```

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](https://faceio.net/) and sign in to your [FACEIO Console](https://console.faceio.net/dashboard) to get the credentials needed to add Facial authentication.

![FaceIO Console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qr0u0fstz5he28fat8f.png)

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](https://pixlab.io/). 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2tvzpvn0fsdtylzajst.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8cbv4hctx3qdtj0fywno.png)

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:

```shell
NEXT_PUBLIC_FACE_IO_ID=your_faceio_app_id_here
```

> 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

```jsx
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 });
  };
```

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

```jsx
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);
    }
  };
```

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.](https://faceio.net/integration-guide)

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:

```jsx
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);
    }
  };
```

`faceio.authenticate` is another built-in function from [FACEIO](https://faceio.net/) 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 the` Sign 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:
```jsx
const toggleSignUp = () => {
    setIsSigningUp((prevState) => !prevState);
  };
```

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

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

**Step 7: Running the application**
Start the development server by running:
```shell
npm run dev
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rshlifmerbtduqt9wz8c.png)

As a new user, click on **Sign Up instead**, then enter your email and password to begin the authentication process with [FACEIO](https://faceio.net/dev-guides).


![prompt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t02mkyq7umnc8bc0fm26.png)

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

![Accept](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tqr2ufy4cmwntionhp78.png)




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

![auth](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pfba17so4sc0vgaq10ff.png)


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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4klglvsczftrop1c8ldk.png)


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


![Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8amrkievv2tb046c4qm.png)

 

And that's it! We have successfully [integrated facial authentication](https://faceio.net/integration-guide) using FACEIO into an employee dashboard web application.


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

```shell
git clone https://github.com/Tabintel/employee-next-app
```
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.](github.com/Tabintel/employee-next-app)


To [get started](https://faceio.net/getting-started) with **FACEIO**, [sign up here](https://console.faceio.net/) 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](https://faceio.net/dev-guides).