adebisi-fa 643e2c60be fix(security,reliability): address code-review findings
HIGH
- async handlers: Express 4 does not catch rejected promises and Node 22
  exits on unhandled rejections — a malformed body ({} to /cropper/dequeue
  etc.) could hang a request or kill the process. Every cropper route is
  now wrapped (throw → 500 via next), request bodies are validated up
  front (400), and JSON.parse of queue items is guarded. Server-wide
  error middleware added, honouring err.status (413 for oversized bodies).
- error pages HTML-escape all interpolated values — the login failure page
  rendered upstream (portal) error text raw.

MEDIUM
- outbound calls carry AbortSignal timeouts (5s OpenFGA verify, 15s
  cropper upstreams) — a hung upstream can no longer pin a request.
- body limits scoped: /cropper gets 50mb (picture data URLs), everything
  else 1mb (previously 50mb globally, memory-DoS surface pre-auth).
- cropper save validates the client-supplied domain against the catalog;
  picture fetches validate the Minio object key (id-domains/ prefix, no ..).

LOW
- static assets served before the session middleware (no PocketBase load
  per asset); landing header builds DOM nodes with textContent instead of
  innerHTML; picture load checks upstream ok.

5 new tests (40 total) covering malformed bodies, malformed queue items,
unknown domains, object-key traversal, and error-page escaping.
2026-10-01 03:12:02 +01:00
2021-12-08 14:35:55 +01:00

YOUreka Tools — Fingerprint Service

A small Express app that hosts the YOUreka tools landing page and the Fingerprint Agent enrollment wizard. Fingerprint capture is performed by the local fpagent binary (Socket.IO on 127.0.0.1:9000); this app provides the login (YOUreka Auth), the permitted domain selection, and the a2loft integration that stores the enrolled fingerprints.

Layout

tools/
├── server.js               # Express app: routes, session middleware, proxies
├── yrka-auth.js            # YOUreka Auth cookie session → PocketBase (authRefresh)
├── permit.js               # OpenFGA domain permission checks + 5-minute cache
├── public/
│   ├── index.html          # landing page (Image Cropper + Fingerprint Agent)
│   └── assets/
│       ├── style.css
│       └── fp/             # /fingerprint enrollment wizard (Vue 2 via CDN)
└── test/                   # node --test suite (fake portal / PocketBase / a2loft)

Running

Prerequisites: Node.js 22+, fpagent installed and running on this laptop (see the fingerprint-agent repository for installation), the U.are.U 4500 scanner attached.

cd tools
npm install
npm start          # → http://localhost:4173

Open http://localhost:4173 and sign in with your YOUreka account.

fpagent must allow the tools origin. On a dev machine run it with: fpagent -allowed-origins http://localhost:4173

How enrollment works

  1. Sign in — YOUreka Auth (portal web-auth redirect). The PocketBase session is kept in an httpOnly cookie and refreshed server-side.
  2. Pick a domain — the hardcoded catalog (UNICAL, Metropolitan Hotel, UCTH, UNICAL Radio International, YOUreka) is filtered against the same portal OpenFGA endpoint fpagent uses (capture_biometric per domain). Results are cached for 5 minutes; the Refresh domains control unlocks once the cache expires. fpagent re-authorizes every capture regardless.
  3. Locate the staff profile — reference + OTP, validated by apis.a2loft.net through a same-origin proxy (the operator's phone comes from the session).
  4. Capture — right thumb and right index finger, one capture at a time, with per-scan progress and cancel; the PocketBase token, domain and reference travel with every fprint-enroll to fpagent.
  5. Save — both templates are posted to a2loft (with a fresh OTP).

Configuration

Everything is hardcoded in tools/server.js by design — port 4173, portal https://portal.yrka.app, PocketBase https://pb.itcass.net, a2loft https://apis.a2loft.net, agent http://127.0.0.1:9000, and the domain catalog. Change them there; there are no environment variables or config files.

The Image Cropper

The /cropper page (Smart Photo Editor) lets operators review, crop and save identity photos. It reuses the YOUreka session for identity, requires the crop_image permission on domain:idid for every proxied action (verified against the portal's OpenFGA endpoint — live on save, 5-minute cached on reads), and keeps the a2loft OTP requirement only on the save step.

Its service credentials are the sole environment variables this app uses (same names the old id-image-bot used — an existing .env is drop-in):

MINIO_ACCESS_KEY=…            MINIO_SECRET_KEY=…
AZURE_STORAGE_CONNECTION_STRING=…
NODERED_HTTP_USERNAME=…       NODERED_HTTP_PASSWORD=…
CARVEKIT_API_KEY=…

Without them the rest of the app works and /cropper routes answer 503. Run locally with cp tools/.env.example tools/.env + your values and node --env-file=tools/.env tools/server.js (or docker run --env-file …).

Tests

cd tools
npm test           # node --test — 35 tests, no network or hardware required

Docker

Docker

The app is stateless, so the container only serves the web front-end; fpagent keeps running on the operator's laptop (the wizard connects to http://127.0.0.1:9000 from the browser, wherever this container lives).

docker build -t yreka-tools .
docker run -d -p 4173:4173 --name yreka-tools yreka-tools

Behind a TLS-terminating proxy the app honours X-Forwarded-Proto (trust proxy is enabled), so session cookies get the Secure flag automatically.

Field walkthrough (real portal, PocketBase, a2loft, and U.are.U 4500): login → permitted-domain filtering → staff lookup → two scanner captures with cancel path → save → reset, all verified end to end via Playwright.

S
Description
The web front-end for YOUreka ID field operations. A small Express app that hosts an authentication-aware landing page, an **Image Cropper**, a **Fingerprint Agent** enrollment wizard, and a public **Help** page.
Readme
920 KiB
Languages
JavaScript 74.7%
HTML 14.7%
CSS 6.9%
Shell 3%
Dockerfile 0.7%