- image installs @dotenvx/dotenvx (pinned) and starts via 'dotenvx run -f .env.production --strict -- node server.js' - only DOTENV_PRIVATE_KEY_PRODUCTION is supplied at runtime; without it the container exits(1) (MISSING_PRIVATE_KEY) instead of booting with undecryptable credentials - .env.production copied with --chown=node:node (the source mode is 600) - .dockerignore excludes .env/.env.local/.env.keys so local envs and the private keys never enter the build context
138 lines
5.2 KiB
Markdown
138 lines
5.2 KiB
Markdown
# 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`](../fingerprint-agent) 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.
|
|
|
|
```sh
|
|
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.
|
|
They live in dotenvx-encrypted files at the repository root — `.env` for
|
|
development, `.env.production` for deployment:
|
|
|
|
```sh
|
|
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`.
|
|
The private decryption keys sit in `.env.keys` (gitignored, never commit it).
|
|
|
|
```sh
|
|
dotenvx run -f .env -- node tools/server.js # local, from the repo root
|
|
dotenvx set MINIO_SECRET_KEY "<value>" -f .env # add/rotate a value
|
|
```
|
|
|
|
The Docker image contains the encrypted `.env.production` and runs through
|
|
`dotenvx`; only the private key is supplied at runtime (never baked in):
|
|
|
|
```sh
|
|
docker build -t yreka-tools .
|
|
docker run -d -p 4173:4173 --name yreka-tools \
|
|
--env DOTENV_PRIVATE_KEY_PRODUCTION="$(grep -oP '^DOTENV_PRIVATE_KEY_PRODUCTION=\K.*' .env.keys)" \
|
|
yreka-tools
|
|
```
|
|
|
|
`dotenvx run --strict` makes the container exit rather than start with
|
|
undecryptable credentials. Note that `--env` values are visible to
|
|
`docker inspect`; on a shared host prefer an env file with restrictive
|
|
permissions (`--env-file deploy.keys`) or your platform's secret store.
|
|
|
|
## Tests
|
|
|
|
```sh
|
|
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).
|
|
|
|
```sh
|
|
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.
|
|
|
|
|