<h1 align="center">Private ID v3 Wasm SDK</h1>

#### <p align="center">Powered by Private Identity® <br/> [https://www.privateid.com](https://www.privateid.com)</p>

<p>
This Private ID v3 Wasm SDK supports NIST 800-63 user registration with identity proofing, and user face login with FIDO Passkey, using Cryptonets fully homomorphically encrypted (FHE) for privacy and security.
</p>

#### FEATURES
<ul>
    <li>Biometric face registration and authentication compliant with IEEE 2410-2021 Standard for Biometric Privacy, and exempt from GDPR, CCPA, BIPA, and HIPPA privacy law obligations. </li>
    <li>Face registration and 1:n face login in 200ms constant time</li> 
    <li>Biometric age estimation with full privacy, on-device in 20ms</li>
    <li>Unlimited users (unlimited gallery size)</li>
    <li>Fair, accurate and unbiased</li>
    <li>Operates online or offline, using local cache for hyper-scalability</li>
</ul>

#### BUILD
<ul>
    <li>Verified Identity</li>
    <li>Identity Assurance</li>
    <li>Authentication Assurance</li>
    <li>Federation Assurance</li>
    <li>Face Login</li>
    <li>Face Unlock</li>
    <li>Biometric Access Control</li>
    <li>Account Recovery</li>
    <li>Face CAPTCHA</li>
</ul>

## Prerequisite

Sign up on the waitlist on https://www.privateid.com to obtain your apiKey.

Installation

```bsh
npm install @privateid/cryptonets-web-sdk
```

Copy the necessary dependencies to the public folder of your app.

```bsh
"prestart": "cp -R ./node_modules/@privateid/cryptonets-web-sdk/wasm public/&& cp -R ./node_modules/@privateid/cryptonets-web-sdk/workers public/",
"prebuild": "cp -R ./node_modules/@privateid/cryptonets-web-sdk/wasm public/ && cp -R ./node_modules/@privateid/cryptonets-web-sdk/workers public/"
```

Add the necessary environment variables on the .env file in the root of your project


```bsh
SKIP_PREFLIGHT_CHECK=true
REACT_APP_API_KEY=
```

Optional environment variable

```bsh
REACT_APP_API_ORCHESTRATION=
REACT_APP_SET_CACHE= true | false (Used for caching by default is true)
```

## Load the WASM Module

The first step is to load and initialize the wasm module.

```javascript
import { loadPrivIdModule } from '@privateid/cryptonets-web-sdk';
// load PrivModules accepts the following:
interface apiUrlProps {
  collections: {
    default: {
      named_urls: {
        base_url: string,
        predict?: string,
        enroll?: string,
        validate_api_key?: string
      };
    };
  };
}

interface loadModulesProps {
  api_url: apiUrlProps; // collection or collections of api url
  api_key?: string; // api key
  api_orchestration_url?: string; // if orchestration will be use and env is not available
  cache_config?: boolean; // if cace_config will be use and env is not available
}
const isSupported = await loadPrivIdModule({
    api_url: {
        collections: {
            default: {
                base_url: process.env.REACT_APP_API_URL,
            }
        }
    },
    // incase ENV variable is not available in your app provide your api key here
    api_key: "API KEY"
}:loadModulesProps);
```

The loadPrivIdModule() function returns an object to check if the device is supporting WASM and WebRTC.  
```javascript
{
    support: boolean,
    message: string,
}
```

## Open or switch camera or close camera

The first step is to load and initialize the wasm module.

#### Open the camera

To open user camera use the openCamera function with element id of the video tag in the DOM
```javascript
import { openCamera } from '@privateid/cryptonets-web-sdk';

const { devices, faceMode } = await openCamera(element);
```

it returns list of the available video devices.

#### Switch to another camera

To switch to another media device use the switchCamera function with the ID of the device to be used

```javascript
import { switchCamera } from '@privateid/cryptonets-web-sdk';

switchCamera(deviceID);

```

for the mobile phone we can select whether it's front or back camera for this we pass 'front' or 'back' to the switch function

```javascript
import { switchCamera } from '@privateid/cryptonets-web-sdk';

switchCamera('front');

```

#### Close the camera

To close user camera use the closeCamera function with element id of the video tag in the DOM. 
```javascript
import { closeCamera } from '@privateid/cryptonets-web-sdk';

closeCamera(element); // Will specifically look for the element id and will close that camera
closeCamera(); // By default will close opened camera using openCamera
```


### isValid  

Live demo:  [https://age.devel.private.id]

isValid only exercises the `is_valid()` call and does not find identity. The function detects if there is a valid face in the camera view or in an ImageData Array. 

isValid accepts three parameters. 
- callback: callback function to perform actions.
- imageData: imageData to perform the face captcha on (optional)
- config: internal configurable settings (optional)

```javascript
import { isValid } from '@privateid/cryptonets-web-sdk';

 await isValid(callback, imageData, config);

```

Face CAPTCHA returns 26 possible coded results. 

| Property | Description                |
|----------|----------------------------|
| -100     | Internal error             |
| -1       | Invalid image              |
| 0        | Valid face                 |
| 1        | Image spoof                |
| 2        | Video spoof                |
| 3        | Too close                  |
| 4        | Too far                    |
| 5        | Close to right edge        |
| 6        | Close to left edge         |
| 7        | Close to top edge          |
| 8        | Close to bottom edge       |
| 9        | Too blurry                 |
| 10       | Eye glasses detected       |
| 11       | Facemask detected          |
| 12       | Chin too far left          |
| 13       | Chin too far right         |
| 14       | Chin too far up            |
| 15       | Chin too far down          |
| 16       | Image too dim              |
| 17       | Image too bright           |
| 18       | Face low confidence value  |
| 19       | Invalid face background    |
| 20       | Eyes closed                |
| 21       | Mouth open                 |
| 22       | Face tilted right          |
| 23       | Face tilted left           |
 


Liveness check 7 possible coded results:
| Property | Description                |
|----------|----------------------------|
| -100     | Invalid Image              |
| -4       | Invalid Face               |
| -3       | Face Too Close To Edge     |
| -2       | Mobile Phone Detected      |
| -1       | No Face Detected           |
| 0        | Real                       |
| 1        | Spoof Detected             |


## Predict Age 
Perform an age estimation using `predictAge` function on all detected faces from image or camera.

The function takes 4 parameters 
| Property      | Description                                                  |
|---------------|--------------------------------------------------------------|
| callback      | The callback function to handle predictAge results.          |
| config        | Internal configurable settings (optional)                    |
| element       | The element ID of the video to be used. (optional)           |
| imageData     | ImageData to perform the agePrediction on (optional)         |

```javascript
import { predictAge } from '@privateid/cryptonets-web-sdk';

await predictAge(predictAgeCallback);

```

## Enroll 
Perform a new enrollment (register a new user) using the `enroll1FA` functio.
The function will need 10 concecutive valid faces to be able to enroll.
We would need to pass the same `enroll_token` on success using configuration. 
If `enroll_token` changes that means we had an invalid image for enroll and started again from the beginning. (Note: Concecutive 10 face needed.)
When enrollment is successful after 10 concecutive valid faces, `enroll1FA` is returning one enrollment image that can be use for document mugshot comparison, etc.

The function takes 3 parameters 
| Property      | Description                                                  |
|---------------|--------------------------------------------------------------|
| callback      | The callback function to handle enroll results.              |
| config        | Internal configurable settings (optional)                    |
| element       | The element ID of the video to be used. (optional)           |

```javascript
import { enroll1FA } from '@privateid/cryptonets-web-sdk';

const image: ImageData | null = enroll1FA(callback, {enroll_token:"token"});

```


## Predict
Perform predict (authenticate a user) using the `predict1FA` function

The function takes 3 parameters 
| Property      | Description                                                  |
|---------------|--------------------------------------------------------------|
| callback      | The callback function to handle enroll results.              |
| config        | Internal configurable settings (optional)                    |
| element       | The element ID of the video to be used. (optional)           |

```javascript
import { predict1FA } from '@privateid/cryptonets-web-sdk';

await predict1FA(callback)

```

## Face Login
Perform Face Login (authenticate a user) using the `faceLogin` function.
Face login performs a more restrictive prediction of face.

The function takes 3 parameters 
| Property      | Description                                                  |
|---------------|--------------------------------------------------------------|
| callback      | The callback function to handle enroll results.              |
| config        | Internal configurable settings (optional)                    |
| element       | The element ID of the video to be used. (optional)           |


```javascript
import { faceLogin } from '@privateid/cryptonets-web-sdk';

await faceLogin(callback)

```

## Continuous Authentication
Perform continuous user prediction (authentication) using the `continuousAuthentication` function. This provides the same status updates as Predict.

```javascript
import { continuousAuthentication } from '@privateid/cryptonets-web-sdk';

continuousAuthentication(callback, configuration, element)
```

The continuousAuthentication function takes 4 parameters

| Property | Description                                |
|----------|--------------------------------------------|
| callback | The callback to be executed on each status |
| configuration | Please use recommended: { input_image_format: "rgba" } | 
| element | (Optional) Element ID of the video tag where the camera is open. |

## isValidPhotoID 

This function finds, crops, align and validate, front and back of the photoID, and returns back a valid cropped and aligned image of the photoID
```javascript
import { isValidPhotoID } from '@privateid/cryptonets-web-sdk';

//Front of DL scan 
const { imageData, croppedDocument, croppedMugshot } = isValidPhotoID("PHOTO_ID_FRONT", documentCallback);

// Back of DL barcode scan
const { croppedBarcode, croppedDocument } = isValidPhotoID("PHOTO_ID_BACK", callback)
```

The isValidPhotoID function takes 3 parameters

| Property | Description                                                                                     |
| -------- | ----------------------------------------------------------------------------------------------- |
| docType  | it can be either PHOTO_ID_BACK (back of the photo ID) or PHOTO_ID_FRONT (front of the photo ID) |
| callback | returns the uuid of the portrait on the Front of the Photo ID                                   |

The function returns:

<b> Front Document Scan (PHOTO_ID_FRONT) </b> <br />
<b>imageData</b>: ImageData datatype of the input image  <br /><br />
<b>croppedDocument</b>: Uint8ClampedArray convertable to base64String of the cropped document. If document scan is unsuccessful, this will return <b>null</b> <br /><br />
<b>croppedMugshot</b>: Uint8ClampedArray convertable to base64String of the cropped face. If document scan is unsuccessful, this will return <b>null</b> <br /><br />
(Note: Please use utilty function below for converting images to base64 string.)

<b> Front DL Callback Data returns: </b>

```javascript
{
    "error": number,
    "payload_type": "face_id",
    "image_width": number,
    "image_height": number,
    "doc_center_x": number,
    "doc_center_y": number,
    "doc_x1": number,
    "doc_y1": number,
    "doc_x2": number,
    "doc_y2": number,
    "doc_x3": number,
    "doc_y3": number,
    "doc_x4": number,
    "doc_y4": number,
    "conf_level": number,
    "cropped_doc_width": number,
    "cropped_doc_height": number,
    "cropped_doc_channels": number,
    "doc_validation_status": number,
    "op_message": string,
    "predict_status": number,
    "face_valid": number,
    "op_status": number,
    "cropped_face_width": number,
    "cropped_face_height": number,
    "cropped_face_size": number,
    "cropped_face_channels": number,
    "status_message": string
}
```
<b> Front DL Callback Op Status equivalent: </b>
| Property | Description                           |
|----------|---------------------------------------|
| -2       | SYSTEM ERROR. Please try again later. |
| -1       | MOVE CLOSER                           |
| 0        | Success                               |
| 3        | ALMOST DONE . . .                     |
| 4        | MOVE JUST A LITTLE CLOSER             |
| 9        | TOO BLURRY                            |
| 18       | SHOW FRONT OF DOCUMENT                |


<b> Back Document Scan (PHOTO_ID_BACK) </b><br />
<b>inputImage</b>: Uint8ClampedArray convertable to base64String of the cropped document.  <br /><br />
<b>croppedDocument:</b>: Uint8ClampedArray convertable to base64String of the cropped document. If barcode scan is unsuccessful, this will return <b>null</b> <br />
<b>croppedBarcode:</b>: Uint8ClampedArray convertable to base64String of the cropped barcode. If barcode scan is unsuccessful, this will return <b>null</b> <br />

<b> Back DL Callback sample return value: </b>
Note: Variables might change depending on the barcode data.

```javascript
{
    "op_status": number,
    "op_message": "",
    "payload_type": "barcode",
    "image_width": number,
    "image_height": number,
    "barcode_conf_score": number,
    "barcode_c_x0": number,
    "barcode_c_y0": number,
    "barcode_x1": number,
    "barcode_y1": number,
    "barcode_x2": number,
    "barcode_y2": number,
    "barcode_x3": number,
    "barcode_y3": number,
    "barcode_x4": number,
    "barcode_y4": number,
    "crop_img_topleft_x": number,
    "crop_img_topleft_y": number,
    "crop_img_botright_x": number,
    "crop_img_botright_y": number,
    "crop_doc_width": number,
    "crop_doc_height": number,
    "crop_doc_bytes": number,
    "crop_doc_channels": number,
    "crop_barcode_width": number,
    "crop_barcode_height": number,
    "crop_barcode_bytes": number,
    "crop_barcode_channels": number,
    "type": string,
    "format": string,
    "documentId": string,
    "customerId": string,
    "firstName": string,
    "lastName": string,
    "middleName": string,
    "expirationDate": string,
    "issueDate": string,
    "dateOfBirth": string,
    "gender": string,
    "eyeColor": string,
    "hairColor": string,
    "height": string,
    "streetAddress1": string,
    "streetAddress2": string,
    "RestStreetAddress1": string,
    "RestStreetAddress2": string,
    "city": string,
    "state": string,
    "postCode": string,
    "issuingCountry": string,
    "firstNameTruncation": string,
    "placeOfBirth": string,
    "auditInformation": string,
    "inventoryControlNumber": string,
    "lastNameAlias": string,
    "firstNameAlias": string,
    "suffixAlias": string,
    "nameSuffix": string,
    "namePrefix": string,
    "barcode_key_string": string,
    "barcode_key_string_encoding": string,
    "barcodeHash64_string": string,
    "barcodeHash128_string": string,
    "status_message": string
}
```

<b> Back DL Callback Op Status equivalent: </b>
| Property | Description                           |
|----------|---------------------------------------|
| -2       | SYSTEM ERROR. Please try again later. |
| -1       | MOVE CLOSER                           |
| 0        | Success                               |
| 3        | ALMOST DONE . . .                     |
| 4        | MOVE JUST A LITTLE CLOSER             |
| 9        | TOO BLURRY                            |
| 10       | MOVE CLOSER TO BARCODE                |
| 18       | PRESENT BARCODE IN BOX                |

### Utility function for convertion of image returned by front and back dl scan
## convertCroppedImage() 
This is a utility function that helps for converting returned image from FRONT DL SCAN and BACK DL SCAN

```javascript
import { convertCroppedImage } from '@privateid/cryptonets-web-sdk';

// FRONT DL SCAN and BACK DL SCAN
// For converting InputImage returned data FRONT DL SCAN and from BACK DL SCAN.
const InputImageBase64String = convertCroppedImage(inputImage, image_height, image_width);

// FRONT DL SCAN
// For converting croppedDocument returned data from FRONT DL SCAN.
// (Note: We would need other variables that is returned in the callback data. crop_doc_width, crop_doc_height)
const FrontCroppedDocumentBase64String = convertCroppedImage(croppedDocument, crop_doc_width, crop_doc_height);

// For converting croppedMugshot returned data from FRONT DL SCAN.
// (Note: We would need other variables that is returned in the callback data. cropped_face_width and cropped_face_height)
const FrontCroppedMugshotBase64String = convertCroppedImage(croppedMugshot, cropped_face_width and cropped_face_height);

// BACK DL SCAN
// For converting croppedBarcode returned data from BACK DL SCAN.
// (Note: We would need other variables that is returned in the callback data. crop_barcode_width and crop_barcode_width)
const CroppedBarcodeBase64String = convertCroppedImage(croppedBarcode, crop_barcode_width, crop_barcode_height);

// For converting croppedDocument returned data from BACK DL SCAN.
// (Note: We would need other variables that is returned in the callback data. crop_doc_width and crop_doc_height)
const CroppedDocumentBase64String = convertCroppedImage(croppedDocument, crop_doc_width, crop_doc_height);
```

## faceCompareLocal
This function compares 2 faces if they are matching. 
In the callback it is returning a conf_score (recommended threshold for faceCompare is 0.25)

```javascript
import { faceCompareLocal } from '@privateid/cryptonets-web-sdk';

faceCompareLocal(callback, imageData1, imageData2, { input_image_format: "rgba" });

```
The faceCompareLocal function takes 4 parameters
| Property | Description                                                                                     |
| -------- | ----------------------------------------------------------------------------------------------- |
| callback  | a function that will handle the result of compare returned                  |
| imageData1 |  imageData datatype of the first image                  |
| imageData2 | imageData datatype of second image |
| config | Additional configuration. Please enter { input_image_format: "rgba" } as a default |

Sample returned data in callback:
```javascript
{
    "error": number,
    "distance_max": number,
    "distance_mean": number,
    "distance_min": number,
    "conf_score": number,
    "face_thresholds_count": number,
    "face_thresholds": [
        number,
        number,
        number
    ],
    "result": number,
    "valid_flag_a": number,
    "valid_flag_b": number
}
```

Licensing Restriction: This product is not licensed for use in regulated gaming. To license in this market, please contact [CentralAMS](https://www.centralams.com/). 

## deleteUser

This function is used for deleting user with the uuid

```javascript
import { deleteUser } from "@privateid/cryptonets-web-sdk";

deleteUser(uuid, callback)
```

Note: Please use Predict to get the user UUID you want to delete and pass it in 

The function returns: <br />
<b>result</b>: status of the validation of the document <br />

| Status | Description       |
| ------ | ----------------- |
| 0      | deletion is successful    |
| -1     | deletion is unsuccessful |

<b>messege</b>: deletion messege<br /> 

## checkPackageSupport

This function checks if the web browser supports WASM based on: https://www.lambdatest.com/web-technologies/wasm
And also checks if WebRTC is supported based on: https://caniuse.com/?search=getusermedia

```javascript
import { checkPackageSupport } from "@privateid/cryptonets-web-sdk";

checkPackageSupport();
```

The function returns: <br />
<b>status</b>: status is a boolean when "True" the browser supports WASM and "False" browser does not support WASM <br />
<b>messege</b>: Messege of the checking of package support. <br /> 

# API Utils

## createUser
This function accepts { `token` (CID or GAN Unique ID/PlayerID), `phone`, `email`, ... } and creates user.

```javascript
import { createUser } from '@privateid/cryptonets-web-sdk';

createUser(parmas)

params = {
    id?: string; // (CID or GAN Unique ID/PlayerID)
    guid?: string;
    phone: string;
    phoneVerified?: boolean;
    email?: string;
    emailVerified?: boolean;
    ssn4?: string;
    ssn9?: string;
    userConsent: boolean;
    userConsentDate: string;
    userFaceEnrollConsent?: boolean;
    consentVersion: string;
    portrait?: string;
};
```

## sendEnrollSms
This function sends SMS to user's phone with a link to acquire front & back of <b>Driver License</b> from mobile. (also enroll face if `faceEnrollConsent` is true).  
From the redirect link in SMS, you can find query string `code`. You can pass it through [verifyToken](#verifytoken) function as a verification code to get user information.  
You can use it if the webcam is lower than <b>2MP</b>.

```javascript
import { sendEnrollSms } from '@privateid/cryptonets-web-sdk';

sendEnrollSms({ id })

{
    id: string; // (CID or GAN Unique ID/PlayerID)
}
```

## verifyToken
This function gets the verification code as parameter and return the user information if it's valid code.
```javascript
import { verifyToken } from '@privateid/cryptonets-web-sdk';

verifyToken({ code, type })

{
    code: string; // verification code
    type: ['phone', 'email']; 
}
```

## uploadPortrait
This function uploads user <b>portrait</b> image.

```javascript
import { uploadPortrait } from '@privateid/cryptonets-web-sdk';

uploadPortrait({ id, portrait })

{
    id: string; // (CID or GAN Unique ID/PlayerID)
    portrait: string // Base64 
}
```

## uploadDL
This function uploads front & back of <b>Driver Licence</b> image.

```javascript
import { uploadDL } from '@privateid/cryptonets-web-sdk';

// Uploading Front DL images
uploadDl({ id, type, image })
	id: string //Required
	type: "frontDLoriginal", "frontDLheadshot", "frontDLcropped"
	image: string // Base64 //Require
 
// Uploading Back DL image
uploadDl({ id, type, image })
	id: string //Required
	type: "backDLoriginal", "backDLbarcode"
	image: string // Base64 //Required 
 
// Uploading Back DL barcode JSON
uploadDl({ id, type, barcode})
	id : string //Required
	type:  "barcodeJson"
	barcode: string //Required
```
| Parameter | Description       |
| -------- | ----------------- |
| id       | Id to be set for customerID. (Required)    |
| type     |"frontDLoriginal", "frontDLheadshot", "frontDLcropped", "backDLoriginal", "backDLbarcode" , "barcodeJson". (required) |
| image    | base64 string of image. (Required for all type exept 'barcodeJson') |
| barcode  | JSON string of the barcode data. (Required for type 'barcodeJson')  |


## updateUser
This function update user preferred data.  
i.e. `address`, `city`, `state`, `country`, `postalCode`, etc.  
This information will be stored in `preferred` field of User object in DB.

```javascript
import { updateUser } from '@privateid/cryptonets-web-sdk';

updateUser({ id, attributes })

{
    id: string; // (CID or GAN Unique ID/PlayerID)
    attributes: {...}; // 
}
```

## getUserStatus
This function will return the status of user.

```javascript
import { getUserStatus } from '@privateid/cryptonets-web-sdk';

getUserStatus({ id  })

{
    id: string; // (CID or GAN Unique ID/PlayerID)
}
```
<b>Response</b>

| <b>Name</b> | <b>Value</b> | <b>Description</b> |
| ----------- | ------------ | ------------------ |
| userRegistered | `true`, `false` | `true` if all user information provided for [createUser](#createuser) function |
| emailVerified | `true`, `false` | Email verified for user |
| phoneVerified | `true`, `false` | Phone number verified for user |
| manualPIIEntry | `true`, `false` | `true` for manual data entry (no Scan ID) |
| userScanID | `true`, `false` | `true` Approved Scan ID |

## validateSecurityToken
This function will validate the returned securityToken.

Note: Token are only valid for 5 minutes starting from the creation of token. After 5 minutes the token will be invalid.

```javascript
import { validateSecurityToken } from '@privateid/cryptonets-web-sdk';

validateSecurityToken({ uuid, token })

{
    puid: string; // UUID of the user
    token: string; // Token from the api call i.e. enroll1Fa/predict1Fa
}
```

<b> Response </b>
| <b>Name</b> | <b>Value</b> | <b>Description</b> |
| ----------- | ------------ | ------------------ |
| securityToken | string      | The securityToken passed in the API call. |
| securityTokenStatus | '1','0' | '1' being TRUE means securityToken is valid. '0' being FALSE means securityToken is invalid. |


## clearCache
This is a utility function that will clear the cache data.

```javascript
import { clearCache } from '@privateid/cryptonets-web-sdk';

clearCache();
```


## generatePasskey
The `generatePasskey` function is used to generate a passkey based on a UUID.

```javascript
import { generatePasskey } from '@privateid/cryptonets-web-sdk';

generatePasskey(uuid);

{
    uuid: string; // The UUID for which you want to generate a passkey (Required)
}
```


## authenticatePasskey
The `authenticatePasskey` function is used to authenticate a generated passkey.

```javascript
import { authenticatePasskey } from '@privateid/cryptonets-web-sdk';

authenticatePasskey(uuid);

{
    uuid: string; // The UUID for which you want to authenticate a passkey (Required)
}
```


## deletePasskey
The `deletePasskey` function is used to delete the generated passkey.

```javascript
import { deletePasskey } from '@privateid/cryptonets-web-sdk';

deletePasskey(uuid);

{
    uuid: string; // The UUID for which you want to delete the passkey (Required)
}
```