This frontend module provides a single publicly accessible page where a health-facility agent or kiosk can:
- Open the device camera and show a live video preview.
- Accept an insuree identifier (UUID).
- Capture a still frame and send it as a base-64 JPEG to the biometric-verification backend module.
- Display whether the person in front of the camera matches the face enrolled at registration time.
No login is required to reach this page.
openimis-fe-biometric_verification_js/
├── src/
│ ├── index.js # Module entry-point & openIMIS contribution
│ └── pages/
│ └── BiometricVerifyPage.jsx # The single public page (must be .jsx for esbuild)
├── vite.config.js # Build config (ESM + CJS outputs, library mode)
└── package.json # @openimis/fe-biometric-verification
Important: the page component must keep the
.jsxextension. Vite/esbuild only enables JSX parsing for files with.jsx(or.tsx) extensions — using.jscauses a parse error at build time.
| Concern | Choice |
|---|---|
| UI framework | React 18 |
| Component library | MUI v7 (@mui/material) + legacy JSS (@mui/styles) |
| Styling | makeStyles from @mui/styles |
| Face detection | @vladmandic/face-api (TinyFaceDetector) - models loaded from CDN |
| WebSocket | Native WebSocket API for real-time streaming |
| API calls | WebSocket for verification streaming |
| i18n | react-intl v6 (via fe-core) |
| Build | Vite 5 (library mode) — outputs dist/index.es.js (ESM) and dist/index.js (CJS) |
| Runtime | Node ≥ 16, npm ≥ 8 |
All @openimis/*, @mui/*, and react* packages are peer dependencies and must not be bundled.
src/index.js exports a factory function BiometricVerificationModule(cfg) that merges a default config object into the openIMIS plugin registry.
The only contribution used is:
core.UnauthenticatedRouter
This tells openimis-fe-core's App.js to mount BiometricVerifyPage at /biometric/verify without requiring the user to be authenticated.
| Path | Component | Auth required |
|---|---|---|
/biometric/verify/:uuid? |
BiometricVerifyPage |
No |
The :uuid parameter is optional. When provided (e.g. /front/biometric/verify/abc-123-def), the insuree UUID field is pre-filled. This enables QR-code or deep-link workflows where the kiosk receives a direct URL with the insuree identifier embedded.
The page uses a two-column layout:
- Left panel (dark teal
#006273): Explanatory section describing the purpose of the verification system — confirms beneficiary identity and liveness to ensure they can receive their benefit package. Includes the openIMIS logo (white-filtered) and feature icons. - Right panel (white): Camera preview, insuree UUID input field (pre-filled from URL if available), and verification controls.
- On mount: Calls
navigator.mediaDevices.getUserMedia({ video: { facingMode: "user" } })and attaches the stream to a<video>element. The preview is mirrored (transform: scaleX(-1)) so it feels natural to the subject. - Pre-fill UUID: If a
:uuidparameter is present in the URL, the insuree UUID field is automatically pre-filled. Otherwise, the user types (or a barcode scanner pastes) the insuree UUID. - Start verification (button click):
- Immediate first capture: A hidden
<canvas>(640 × 640 px) captures the current video frame (mirrored), serialises it to base-64 JPEG (quality 0.92), and sends it via the GraphQL mutation. - Auto-repeat every 15 seconds: A
setIntervalfires every 15 seconds, capturing and sending a new frame automatically. - The UUID input is disabled during verification to prevent changes mid-session.
- A "Stop Verification" button (red) appears, allowing the user to halt the periodic captures.
- Immediate first capture: A hidden
- Results: Each verification displays one of three states:
- Verified (green border + light green background + CheckCircle icon)
- Rejected (red border + light red background + Cancel icon)
- Error (grey border + white background + Cancel icon)
- The total number of captures is displayed in the result panel.
The page uses openIMIS branding and follows the standard color palette:
- Page background:
#dbeef0(light teal — openIMISbackgroundColor) - Left panel background:
#006273(dark teal — openIMISprimaryColor) - Primary:
#006273— used for buttons, camera border, titles - Error:
#801a00(openIMISerrorColor) — used for "Stop" button - Text:
#003d4a(dark blue — openIMISfontColor) - Borders/inputs:
#b7d4d8(medium teal — openIMISheaderColor) - Font: Rubik / Roboto (matches openIMIS typography)
The openIMIS logo is displayed in the left panel (white-filtered). Material-UI icons (CheckCircleIcon, CancelIcon, VerifiedUserIcon) illustrate features and results.
Synchronous verification — returns result immediately.
mutation VerifyFace($uuid: String!, $frame: String!) {
verifyFace(uuid: $uuid, frame: $frame) {
verified # Boolean — true if face matches
confidence # Float — percentage (0–100)
distance # Float — raw similarity distance
provider # String — e.g. "deepface", "aws_rekognition"
error # String | null — error message if verification failed
}
}Arguments:
uuid(String!) — Insuree UUIDframe(String!) — Base64-encoded JPEG (with or withoutdata:image/jpeg;base64,prefix)
Behavior:
- Executes synchronously — verification happens in real-time
- Returns result immediately (no polling required)
- Anonymous access allowed (whitelisted in
JWT_ALLOW_ANY_CLASSES) - Authenticated users subject to permission checks if
gql_mutation_verify_face_permsis configured
The mutation is resolved by openimis-be-biometric-verification_py (Python/Django backend module, mounted as a local volume in the Docker compose setup).
The backend module lives at ../openimis-be-biometric-verification_py relative to the compose file. It is included in the main openimis-be_py Django application as a local editable install. The compose service is named backend and is reachable from the frontend container at http://backend/api/graphql.
The backend is expected to:
- Store a reference face photo for each insuree at enrolment time.
- Accept the
verifyFacemutation, decode the base-64 frame, run a face-matching algorithm, and return the structured response above.
# Install dev deps (peer deps come from the host app)
npm install
# Watch & rebuild on change (vite build --watch)
npm start
# Production build (vite build)
npm run buildThe build is configured in vite.config.js using Vite's library mode.
@vitejs/plugin-react handles JSX transformation via esbuild — no Babel config is needed.
All peer dependencies are declared as Rollup externals inside the Vite config so they are never bundled.
The built artefacts land in dist/. To use the module inside openimis-fe_js, add it to openimis.json as a local path dependency (file: reference) and register BiometricVerificationModule in the openIMIS module list.
In openimis-dev.json (inside openimis-fe_js), add an entry that points to this module's source directory:
{
"name": "BiometricVerificationModule",
"npm": "@openimis/fe-biometric-verification@file:/path/to/openimis-fe-biometric_verification_js"
}entrypoint-dev.js rewrites openimis-dev.json with file: references for all cloned/local modules. openimis-config-vite.js reads the rewritten JSON and injects Vite resolve.alias entries pointing into the src/ directory of each file: module, enabling hot-reload of sources without a prior npm run build.
- No authentication — the route must be reachable without a valid session token so it can run on a public kiosk.
- No navigation chrome — the page is self-contained; do not add the standard openIMIS sidebar/header.
- Minimal dependencies — only peer deps already shipped with
openimis-fe_jsshould be used. - Camera cleanup — the
useEffectmust stop all media tracks on unmount to avoid leaving the camera LED on. - Image orientation — the canvas draw step un-mirrors the preview so the JPEG sent to the backend matches the orientation of the enrolled photo.
.jsxextension — always use.jsx(not.js) for files that contain JSX syntax. esbuild does not parse JSX in.jsfiles.