React (Vite react-ts template)

Follow these steps to get started with ReactJS from Vite react-ts template.

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

    pnpm create vite livenesstest --template react-ts

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

    livenesstest/
        |
        +-- node_modules/
        |
        +-- public/
        |    |
        |    +-- favicon.svg
        |    |
        |    \-- icons.svg
        |
        +-- src/
        |    |
        |    +-- assets/
        |    |
        |    +-- App.css
        |    |
        |    +-- App.tsx
        |    |
        |    +-- index.css
        |    |
        |    \-- main.tsx
        |
        +-- .gitignore
        |
        +-- package-lock.json
        |
        +-- package.json
        |
        +-- README.md
        |
        +-- tsconfig.app.json
        |
        +-- tsconfig.json
        |
        +-- tsconfig.node.json
        |
        \-- vite.config.ts
  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 new file with name useLiveness.ts inside src folder.

    livenesstest$ touch src/useLiveness.ts
  6. Add the following code to the useLiveness.ts file inside src folder.

    import * as React from "react";
    import Builder from "@verihubs/liveness";
    
    export function useLivenessSDK() {
      const sdkRef = React.useRef<ReturnType<Builder["build"]> | null>(null);
      const [image, setImage] = React.useState("");
      const [data, setData] = React.useState("");
    
      React.useEffect(() => {
        sdkRef.current = 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();
    
        return () => {
          sdkRef.current?.destroy();
          sdkRef.current = null;
        };
      }, []);
    
      React.useEffect(() => {
        const listener = ({
          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":
              alert(subject);
              sdkRef.current?.destroy();
              break;
    
            case "Verification.Success": {
              window.setTimeout(() => {
                setImage(`data:image/png;base64,${data.image.url}`);
                setData(JSON.stringify(data, undefined, 2));
                sdkRef.current?.destroy();
              }, 1500);
    
              break;
            }
    
            default:
              console.log({ data, subject });
              break;
          }
        };
    
        window.addEventListener("message", listener);
    
        return () => {
          window.removeEventListener("message", listener);
        };
      }, []);
    
      const start = React.useCallback(() => {
        sdkRef.current?.start();
      }, []);
    
      const destroy = React.useCallback(() => {
        sdkRef.current?.destroy();
      }, []);
    
      return {
        start,
        destroy,
        image,
        data,
      };
    }
    

    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. Replace App.tsx by the code below:

    import './App.css';
    import { useLivenessSDK } from './useLiveness';
    
    function App() {
      const { start, image, data } = useLivenessSDK();
    
      return (
        <main>
          <button type="button" onClick={start}>
            Start Liveness
          </button>
    
          {image && <img src={image} alt="Liveness result" />}
    
          {data && <pre>{data}</pre>}
        </main>
      );
    }
    
    export default App;
    
  8. Add the dependency to optimizeDeps.include and build.commonjsOptions.include in vite.config.js with code below:

    export default defineConfig({
      plugins: [react()],
      optimizeDeps: {
        include: ["@verihubs/liveness"],
      },
      build: {
        commonjsOptions: {
          include: [/liveness/, /node_modules/],
        },
      },
    });

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

livenesstest$ pnpm dev