fix(biometric-puzzles): wire FacePuzzle + HandGesturePuzzle to server validation (Bug 4, 2026-05-12)#90
Conversation
… validation Closes Bug 4 (P1) from the 2026-05-12 ML review: the biometric-puzzles training surface called `onSuccess()` purely client-side after local MediaPipe detection. Anyone could mock the component and "pass" any challenge — no audit trail, no server-side structural sanity checks. Wiring ------ * New hook `useBiometricPuzzleServer` posts a single completed challenge to `/biometric/puzzles/verify-challenge` (identity-core-api proxy → biometric-processor `/liveness/verify-challenge`). Only a server-200 with `verified=true` resolves the puzzle. Other outcomes surface via `formatApiError` + `t()` per `feedback_no_hardcode`. * New mapper `puzzleServerAction` translates the web's local enums (ChallengeType for face, BiometricPuzzleId for hand) into the lower_snake_case ChallengeType strings the server enum expects. Local variants without a 1:1 server counterpart (CLOSE_LEFT/RIGHT, LOOK_UP/DOWN, individual brow raises, NOD, SHAKE_HEAD, HAND_TRACE_TEMPLATE) return `null` from the mapper and the puzzle resolves locally — the server can't express those challenges today. * `FacePuzzle.tsx` + `HandGesturePuzzle.tsx`: on local completion, set `serverVerifying=true`, await the server round-trip, then `onSuccess` or `onError` based on the verdict. Visual: an "Verifying with server…" Alert appears between local-pass and server-verdict. * i18n: 3 new keys (`verifyingServer`, `serverRejected`, `serverError`) added to en.json + tr.json — no hardcoded strings. Graceful degradation -------------------- The companion biometric-processor PR added the `/liveness/verify-challenge` route, but the identity-core-api proxy at `/biometric/puzzles/verify-challenge` is NOT included in this PR (it lives in another repo and that repo currently has unrelated in-flight work). To keep the training UI functional during rollout, `useBiometricPuzzleServer` catches HTTP 404 from the proxy path, logs a one-shot console warning describing the operator action needed, and resolves the puzzle as a soft-pass. Once the proxy lands, the soft-pass path stops being exercised. Tests ----- * `puzzleServerAction.test.ts` — 24 assertions pinning every face + hand mapping (happy paths) and explicitly checking the unmapped variants return null (regression guard). * Type-check: `npx tsc --noEmit` clean. * `vitest run src/features/biometric-puzzles` — 28 pass / 1 pre-existing PeekABoo-detector failure unrelated to this PR. Operator action items --------------------- 1. Land the matching `biometric-processor` PR (fix/2026-05-12-liveness-and-puzzles). 2. Add the identity-core-api proxy route `POST /api/v1/biometric/puzzles/verify-challenge` that forwards to biometric-processor `/liveness/verify-challenge` (mirrors the existing `BiometricServiceAdapter.generateLivenessPuzzle` pattern). 3. Deploy web-app. With the proxy in place, server validation engages automatically; without it, the soft-pass console warning fires. Memory rules respected ---------------------- * `feedback_no_hardcode`: every user-facing string is `t()`'d with keys in en.json + tr.json. No English literals shipped. * `feedback_spoof_detector_architecture`: algorithms still live in spoof-detector / biometric-processor; this PR is pure plumbing. * `feedback_git_push`: bare `git push`, no `2>&1`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Merge order note (2026-05-12) This PR depends on two upstream PRs that must merge first to avoid 404-soft-pass mode:
The identity-core-api proxy ( |
…sas.com root Source for the verify-widget html/index.html landing block that was deployed to verify.fivucsas.com today. main.tsx flips data-mounted="true" on first paint so the static landing hides for iframe/OAuth consumers without flash; direct visitors and crawlers see brand, 3 method cards, 10 auth-method pills, integration snippet, footer. Robots lifted to "index, follow"; description + canonical + og + twitter + JSON-LD SoftwareApplication added. /login behaviour unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a "FIVUCSAS suite" footer block above the existing status tile
with pill links to fivucsas.com / Demo / Widget / amispoof, and turns
the status tile itself into a clickable link to status.fivucsas.com.
Closes the navigation gap supervisor flagged — previously the dashboard
had zero outbound link to landing or sister sites.
i18n keys added to en.json + tr.json (sidebar.suite + sidebar.suite{Home,Demo,Widget}).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…at build The verify build sets root=src/verify-app, so Vite's envDir defaulted there (no .env present) and VITE_API_BASE_URL was undefined at build time. Since PR #62 centralized that var behind src/config/env.ts which throws at module load when it's unset, the verify bundle crashed on boot and React never mounted — verify.fivucsas.com/login showed only the static landing for ~11 days. Point envDir at the project root so .env.production is loaded like the other builds. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… error A direct top-level visit to verify.fivucsas.com (root, no params) mounted VerifyApp, which renders a "missing parameters" Error alert with no session/client context — so visitors saw an error. Only mount React when there's a real surface (hosted /login, a framed widget, or a widget invoked with params); otherwise leave the static #verify-landing visible. Verified live: root → landing (no error), /login → mounts, ?session_id → widget.
|
Superseded — closing. The biometric-puzzle liveness issue this PR targeted (Bug 4, 2026-05-12) is resolved on |
Summary
Closes Bug 4 (P1) from the 2026-05-12 ML review. The biometric-puzzles training surface (
/biometric-puzzles) calledonSuccess()purely client-side after local MediaPipe detection — anyone could mock the component and pass any challenge. Now every completion is round-tripped through/biometric/puzzles/verify-challengefor structural validation, andonSuccessonly fires on server-200.useBiometricPuzzleServerposts the challenge result.puzzleServerActiontranslates web enums → serverChallengeTypestrings.FacePuzzle.tsx+HandGesturePuzzle.tsxawait server validation, show "Verifying with server…" Alert, then resolveonSuccess/onError.__tests__/puzzleServerAction.test.tspinning every face + hand mapping.Companion biometric-processor PR adds the
/liveness/verify-challengeroute: Rollingcat-Software/biometric-processor#102Graceful degradation
The identity-core-api proxy
/biometric/puzzles/verify-challengeis NOT in this PR (it lives in another repo with unrelated in-flight work). To keep the training UI functional during rollout, the hook catches HTTP 404 and soft-passes with a one-shot console warning. Operator action: add the proxy when ready; soft-pass stops being exercised at that point.Test plan
npx tsc --noEmit— cleannpx vitest run src/features/biometric-puzzles/__tests__/puzzleServerAction.test.ts— 24 passnpx vitest run src/features/biometric-puzzles— 28 pass / 1 pre-existing PeekABoo failure (unrelated)POST /api/v1/biometric/puzzles/verify-challenge🤖 Generated with Claude Code