Next JS (Recommended default template)

Follow these steps to get started with NextJS template.

  1. Set up the workspace by running the following command:

    pnpx create-next-app@latest livenesstest

    A folder/directory named livenesstest should appear and the folder/ directory structure should look like this:

    livenesstest/
        |
        +-- node_modules/
        |
        +-- public/
        |    |
        |    +-- file.svg
        |    |
        |    +-- globe.svg
        |    |
        |    +-- next.svg
        |    |
        |    +-- vercel.svg
        |    |
        |    \-- window.svg
        |
        +-- app/
        |    |
        |    +-- favicon.ico
        |    |
        |    +-- global.css
        |    |
        |    +-- layout.tsx
        |    |
        |    \-- page.tsx
        |
        +-- .gitignore
        |
        +-- AGENTS.md
        |
        +-- CLAUDE.md
        |
        +-- eslint.config.mjs
        |
        +-- next-env.d.ts
        |
        +-- next.config.ts
        |
        +-- package.json
        |
        +-- package-lock.json
        |
        +-- postcss.config.mjs
        |
        +-- README.md
        |
        \-- tsconfig.json
  2. Extract file Verihubs WebSDK Liveness [version] [organization].zip file. Rename build folder/directory as liveness.

  3. Put extracted liveness folder/directory that contains the liveness engine and builders like builder.js, into the public folder/directory of project folder/directory (livenesstest).

  4. Add the liveness package to package.json by running this command inside livenesstest React.js project.

    livenesstest$ pnpm i file:public/liveness

    This will add @verihubs/liveness dependency in package.json.

  5. Create liveness.tsx file inside src folder

  6. Add the following code to the liveness.tsx file inside src folder.

    "use client";
    import { useCallback, useEffect, useState } from "react";
    import Builder from "@verihubs/liveness";
    import Image from "next/image";
    
    export default function Liveness() {
      const [image, setImage] = useState<string | null>(null);
      const [data, setData] = useState("");
      const LivenessSDK = new Builder()
        .setInstruction(["look_left", "look_right"], {
          commands: ["open_mouth"],
          seedLimit: 1,
        })
        .setProxyMiddleware({
          PassiveLiveness: {
            url: "http://localhost:8888/liveness/face",
            headers: {
              "App-ID": "<replace-me-with-app-id>",
              "api-key": "<replace-me-with-api-key>",
            },
          },
          License: {
            url: "http://localhost:8888/license/{license_id}/check",
            headers: {
              "App-ID": "<replace-me-with-app-id>",
              "api-key": "<replace-me-with-api-key>",
            },
          },
        })
        .setTimeout(60000)
        .setURL("./liveness")
        .setVirtualCameraLabel(["OBS", "Virtual"])
        .build();
    
      useEffect(() => {
        const livenessMessageListener = ({
          data: { data, subject },
        }: {
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          data: { data: any; subject: string };
        }) => {
          switch (subject) {
            case "Verification.Verbose":
              console.log("[Verbose]", data);
              break;
    
            case "Camera.NotAllowed":
            case "Camera.NotFound":
            case "Camera.PermissionDenied":
            case "ScreenOrientation.NotAllowed":
            case "Verification.Disrupted":
            case "Verification.Timeout":
              console.log({ data, subject });
              alert(subject);
              LivenessSDK.onDestroy();
              break;
    
            case "Verification.Success":
              setImage(`data:image/png;base64,${data.image.url}`);
              setData(JSON.stringify(data, undefined, 2));
              LivenessSDK.onDestroy();
              break;
    
            default:
              console.log({ data, subject });
              alert(subject);
              break;
          }
        };
        window.addEventListener("message", livenessMessageListener);
    
        return () => {
          window.removeEventListener("message", livenessMessageListener);
        };
      }, [LivenessSDK]);
    
      const doLivenessVerification = useCallback(() => {
        LivenessSDK.onStart();
      }, [LivenessSDK]);
    
      return (
        <div>
          <button onClick={doLivenessVerification}>
            Run Liveness Verification
          </button>
          <br />
          {image && <Image src={image} alt="" />}
          <div className="data-result">{data}</div>
        </div>
      );
    }
    

    Replace proxy middleware URLs with a URL that points to the correct endoints, and replace <replace-me-with-app-id> and <replace-me-with-api-key> with the appropriate App-ID and API-Key for the passive liveness (remove the headers if the api doesn't need credentials or the url is pointing towards an intermediate endpoint).

    🚧

    Look Out!

    To not accidentally leak of AppID and APIKey for passive liveness, always pass them to an intermediate endpoint (like a proxy middleware) and append the App-ID and API-Key header before reaching Verihubs' passive liveness service in production. Additional data can also be passed to this intermediate endpoint for end user identification.

    For more information about creating a Proxy Middleware, refer to Proxy Middleware.

    For API documentation of the Builder, refer to Builder.

    📘

    Info

    Check System Messages for full system message references.

  7. Populate the page.tsx component logic by the code below:

    "use client";
    import dynamic from "next/dynamic";
    
    const Liveness = dynamic(() => import("./liveness"), { ssr: false });
    
    export default function Home() {
      return (
        <div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
          <main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
            <Liveness />
          </main>
        </div>
      );
    }
    

To try the above implementation you can run the following command from the livenesstest project:

livenesstest$ pnpm dev

What’s Next

Protect credential using Proxy Middleware