OCR & Biometrics

This section provides more information on how to use the FrankieOne IDV solution through OneSDK OCR and Biometrics components.

ModuleDescription
OCRThe OCR module captures an ID document and performs OCR extraction. This module returns a result object containing the details extracted from the document.
Optionally, you can also enable a review page that presents the OCR results back to the user to confirm and edit if needed.
BiometricsThe Biometrics module captures a live selfie/video from the user and performs liveness detection and facial comparison against the captured ID.
For the purpose of facial comparison, you are required to run OCR first, then the Biometrics component.

Learn more by visiting the following topics:

Capturing document details via OCR

Capturing face details via Biometrics

Capturing document details via OCR

Use the OneSDK OCR component to extract document details from a document image supplied by the user. The OCR component dynamically loads the OCR provider configured for your account. The provider can be modified even after your application is live in production.

To implement the OCR component, you can either utilize a headed or headless version of the OCR component.

In a headed implementation, a pre-built UI is provided to capture the information via OCR, and then the information is passed to OneSDK for processing.

In a headless implementation, you implement your own UI to capture document images, and OneSDK handles the process of communicating with the FrankieOne platform and evaluating the verification results.

A typical OCR flow consists of the following steps:

  1. Capture an image of a physical identity document.
  2. If required, capture a second image. For example, the back side of a driver's license.
  3. Obtain document OCR results, with all data extracted from the images.

Handling image capture

Headed implementation

A headed integration provides a ready-to-use frontend component to capture ID images within OneSDK. You need to mount the OCR component to use the headed version:

ocr.mount(“#<container>“)

Headless implementation

For the headless integration, you need to start the OCR component and listen to the event below to handle the image capture:

input_required event

ocr.on('input_required', (info, status, callback) => {
  // your code here
});

The input_required event is triggered when you need to supply an ID image file to the OCR component. You need to implement your own UI to capture an image from users and pass it through the event.

This event contains the following arguments:

Argument nameDescription
inputInfoAn object describing the document image to request from the user. The object contains two properties, documentType, which may be one of "PASSPORT" or "DRIVERS_LICENCE", and side, which may be one of "front" or "back".
statusAn object that contains the statuses of any previous calls.
callbackA callback function that accepts the captured image data as an instance of the File interface.

Valid statuses

ValueDescription
AWAITING_DOCUMENT_OCROneSDK has captured a document but OCR process is yet to run. Default status if we get errors or user abandons process
AWAITING_DOCUMENT_UPLOAD_FRONTCapture of front needed
AWAITING_DOCUMENT_UPLOAD_BACKCapture of back needed
COMPLETE_OCROCR results are available
AWAITING_DOCUMENT_UPLOAD_FAILED_OCRCapture successful, but an error happened during upload, potentially due to a bad scan
AWAITING_DOCUMENT_UPLOAD_INVALID_TYPEDocument type of the scan seems to be invalid
DOCUMENT_INVALID_EXCEEDED_SIZE_LIMITFile size exceeds the limits supported by third party OCR provider
DOCUMENT_INVALID_INCORRECT_FILE_FORMATFile format is not accepted by the third party OCR provider
AWAITING_OCR_RESULTS_PROVIDER_OFFLINEOneSDK has captured a document but there was a communication issue uploading the document or retrieving the result

The following code example implements an input_required listener that supports passports and driver's licenses.

ocr.on("input_required", async (inputInfo, status, provideFile) => {
  
  const { documentType, side } = inputInfo;
  
  // documentType will initially be null, until the type is inferred from the first provided scan
  if (documentType === "PASSPORT") {
    // present UI to capture a passport image
  } else if (documentType === "DRIVERS_LICENCE") {
    // check which side of the drivers licence is required
    if (side === "front") {
      // present UI to capture the licence's front side
    } else if (side === "back") {
      // present UI to capture the licence's back side
    }
  } else {
    // present UI to capture any type of identity document
  }

  // Your use interface you should capture an image and provide it to OneSDK as a File object.
  // https://developer.mozilla.org/en-US/docs/Web/API/File
  //
  // For example, your interface may use the Image Capture API to obtain the image data, though use of this API is not required.
  // See https://developer.mozilla.org/en-US/docs/Web/API/MediaStream_Image_Capture_API
  // const blob = // ...
  // provideFile(blob)

  // example of capturing a blob image from browser
  navigator.mediaDevices.getUserMedia({ video: true }).then((mediaStream) => {
    // Do something with the stream.
    const track = mediaStream.getVideoTracks()[0];
    let imageCapture = new ImageCapture(track);
    imageCapture.takePhoto().then((file) => provideFile(file));
  });
});

Access the OCR flow status

The following example accesses the status of the OCR flow.

const { getValue } = ocr.access('status');

const status = getValue();

Re-attempt an image capture

Document detection might fail due to a low quality or blurry image, or an invalid document.

In the headed integration, the pre-built UI will automatically ask users to capture another image.

In the headless integration, the input_required event will get triggered with the same arguments, so that you can capture another image and pass it through.

The following example uses the status argument to determine whether to prompt the user to try again.

ocr.on("input_required", (info, status, provideFile) => {
  
  let message = "";
  if (status === oneSdkOcr.statuses.DOCUMENTS_INVALID) {
    // IF OCR EXTRACT ISNT A VALID DOCUMENT TYPE
    message = `Oops, seems like that wasn't a ${info.documentType}, try again.`;
  } else if (status === oneSdkOcr.statuses.DOCUMENTS_UPLOAD_FAILED) {
    // IF OCR EXTRACT FAILED TO POOR IMAGE QUALITY
    message = `Oops, seems like that wasn't very clear, try again.`;
  } else {
    // WHEN EVERYTHING WORKS FINE
    message = info.documentType 
      ? `Alright, give us the ${info.side} of your ${info.documentType}`
      : "Alright, give us a scan of either your Passport or your Drivers Licence";
  }

  showDialog(message, provideFile);
});

Obtaining the OCR results

results event

ocr.on('results', (document) => {
  // your code here
);

The results event is triggered when OCR is complete and data was extracted from the supplied images.

Your event listener should be a callback of the form (document) => void.

ParameterDescription
documentA Document object. See the table below.

error event

ocr.on('error', (error) => {
  console.error(error.message)
});

The error event is triggered when the OCR component encounters a problem it cannot recover from.

Your event listener should be a callback of the form (error) => void.

ParameterDescription
errorAn object with a message property containing a string and a payload property containing an object with more details about the error.

Complete example for headless implementation

Use the component('ocr', options?) method to instantiate the OCR component.

// 1. Obtain the OCR component
const config = {
  dummy: true // Remove this in production
};
const oneSdk = await OneSdk(config);
const ocr = oneSdk.component("ocr");

// 2. Register event listeners

oneSdkOcr.on("input_required", (info, status, provideFile) => {
  dialog(`Please provide the ${info.side} of your ${info.documentType}`, (selectedFile) => {
    provideFile(selectedFile)
  });
});

oneSdkOcr.on("results", ({ document }) => {
  dialog(`Please confirm your information: ${document}`, (okOrNot) => {
    if (okOrNot) gotoNextPage();
    else oneSdkOcr.start() // restart flow
  })
});

oneSdkOcr.on("error", ({ message }) => {
  alert(`There was an issue (${message}). We'll skip OCR for now.`);
  gotoNextPage();
});

// 3. Start the OCR flow

oneSdkOcr.start();

Capturing face details with Biometrics

Use the OneSDK Biometrics component to capture a selfie/video from the user and perform a comparison against the ID that was captured during OCR. The Biometrics component dynamically loads the Biometrics provider configured for your account. The provider can be modified even after your application is live in production.

Create Biometrics Component

Create a DOM container element on your onboarding web page where you want the component to be rendered.

<div id="biometrics-container"></div>

Similar to the OCR component we need to create a biometrics component.

const biometrics = oneSdk.component("biometrics");

Obtaining the results

Listen to the result event to get notified when biometrics results are available.

function startBiometrics(oneSdk) {
  const biometrics = oneSdk.component("biometrics");
  biometrics.on("results", ({ checkStatus, processing }) => {
    // Decide whether to proceed to the next stage of the onboarding process
    // depending on whether biometrics verification was successful.
    if (processing) {
      appendToFileInfo(`Biometrics result ${processing} ${checkStatus}`);
      // access the individual object to represent the user being onboarded
      const individual = oneSdk.individual();
      
      // Submit all information, making sure consent has been captured
      individual.addConsent();
      // You may request checks be run with the optional parameter {verify: true}. This method will return a CheckSummary object in this case and will run the entityProfile for the customer
      individual.submit({
        verify: true,
      });
    } else {
      appendToFileInfo(`Biometrics result received with unknown results`);
    }
  });

Mount the Biometrics component and await results

Start the Liveness Capture by mounting the component in your container element.

biometrics.mount('#biometrics-container');

Listen for the ready event to know when the biometrics capture interface is ready for use.

biometrics.on("ready", () => {
    // If you provided your own loading state it can now be hidden.
    appendToFileInfo("Biometric Ready");
  });