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 the 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. Add an import to main.tsx file inside src folder.

    import Builder from '@verihubs/liveness';
  6. Add the following code to the main.tsx file inside src folder.

    window.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();

    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.

  7. Add import for React in App.tsx.

    import React from 'react';
  8. Populate the App component logic by the code below:

    function App() {
        const [image, setImage] = React.useState('');
        const [data, setData] = React.useState('');
    
        React.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);
                    window.LivenessSDK.onDestroy();
                    break;
        
                case 'Verification.Success':
                    setTimeout(function () {
                        setImage(`data:image/png;base64,${data.image.url}`);
                        setData(JSON.stringify(data, undefined, 2));
                        window.LivenessSDK.onDestroy();
                    }, 1500);
                    break;
        
                default:
                    console.log({ data, subject });
                    alert(subject);
                    break;
                }
            };
            window.addEventListener('message', livenessMessageListener);
        
            return () => {
                window.removeEventListener('message', livenessMessageListener);
            };
        }, []);
    
        const doLivenessVerification = React.useCallback(() => {
            window.LivenessSDK.onStart();
        }, []);
    
        return (
    📘

    Info

    Check System Messages for full system message references.

  9. Replace the jsx component inside App.tsx with the code below:

        return (
        <div>
            <button onClick={doLivenessVerification}>
            Run Liveness Verification
            </button>
            <br />
            <img src={image} alt="" />
            <div className="data-result">{data}</div>
        </div>
        );
  10. Replace the whole content of App.css with the code below:

    .data-result {
        font-family: 'Courier New', Courier, monospace;
        white-space: pre-wrap;
        background-color: #0002;
        overflow: auto;
    }
  11. 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/],
        },
      },
    });
  12. Create a new file named window.d.ts inside your src directory (e.g., src/types/window.d.ts) and add the following code to the file to extend the Window interface:

    import type { BuilderOutput } from "@verihubs/liveness";
    
    // src/window.d.ts
    export {};
    
    declare global {
      interface Window {
        LivenessSDK: BuilderOutput;
      }
    }
    

    Note: Version below 1.9.2 need implement LivenessSDK as any.

  13. Ensure TypeScript recognizes the new file by adding it to the include array in your tsconfig.app.json file.

    {
      "compilerOptions": { ... },
      "include": ["src", "src/types/window.d.ts"],
    }

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

livenesstest$ pnpm run dev

What’s Next

Protect credential using Proxy Middleware