Skip to content

feat(rayverify): identity capture flow on mobile, and fix the body limit - #195

Merged
SishirP17 merged 1 commit into
mainfrom
feat/identity-capture-ui
Aug 5, 2026
Merged

feat(rayverify): identity capture flow on mobile, and fix the body limit#195
SishirP17 merged 1 commit into
mainfrom
feat/identity-capture-ui

Conversation

@SishirP17

Copy link
Copy Markdown
Collaborator

The bug first, because it makes the rest possible

The identity routes accept a base64 selfie, but the app-wide JSON body cap is 100KB, which no photo can fit. Every capture would have returned 413 before reaching the route. #193 shipped dead on arrival, and none of its 23 tests caught it because they all post tiny fixtures.

Fix: identity gets its own 3MB parser mounted ahead of the global one (same pattern the Stripe webhook already uses), so the larger limit applies to that path alone rather than raising the ceiling for every route in the app. The decoded cap drops from 4MB to 2MB, which is still far more than face matching needs. There is now a test that posts a realistically sized payload, which is the test that would have caught it.

The capture flow

Consent → enrol a reference photo → check against it. Reachable from Me → Identity check. The order is enforced server-side too; the screen just makes it legible.

Captures are compressed hard (quality 0.5) on purpose: matching needs resolution around the face, not a printable image, and a small payload uploads far more reliably from a phone in somebody's car. Oversized captures are caught on the device so a caregiver sees a retake prompt rather than a confusing 413.

Two things the screen says out loud

A failed match never implies fraud. The likeliest cause is lighting or an angle, and an app is the wrong place to make that call. There's a test asserting the copy contains no accusatory wording.

Liveness is not built, and the screen says so: "does not yet detect whether a photo was taken of a screen or a printed picture". The status endpoint already reported livenessSupported: false; now a human can see it too.

Withdrawing consent is one tap from the same screen, and a failed deletion says so rather than claiming the photo is gone.

Expo Go

expo-camera (~17.0.10, SDK 54) runs in Expo Go, so this is testable on a phone without a custom native build. Liveness is the part that would have needed one.

Tests

18 new: step derivation including consent-always-first, outcome copy for every branch, unconfigured never presented as a result, upload size guard with headroom under the server's parser limit, and the two body-limit regression tests. All four suites, typecheck, lint, sql:scan, security:scan pass.

Still needed before it works end to end

Owner-side, unchanged from before: IDENTITY_VERIFICATION_PROVIDER=rekognition and DOCUMENTS_S3_BUCKET in Vercel prod, AWS IAM with rekognition:CompareFaces plus s3:DeleteObject, and an AWS BAA covering Rekognition. Migrations are already applied.

Until the provider is set, the flow still works: consent and enrolment succeed, and a check returns not_configured, which the screen presents as "not switched on, nothing was checked" rather than as a pass or a failure.

Two things, because the second makes the first possible.

The identity routes accept a base64 selfie, but the app-wide JSON body cap is
100KB, which no photo can fit. Every capture would have returned 413 before
reaching the route: the feature shipped dead on arrival and no test caught it
because they all posted tiny fixtures. Identity now gets its own 3MB parser
mounted ahead of the global one, so the larger limit applies to that path
alone rather than raising the ceiling for the whole app, and the decoded cap
drops from 4MB to 2MB, which is far more than face matching needs. There is
now a test that posts a realistically sized payload.

The mobile flow is consent, then enrol a reference photo, then check against
it, reachable from the Me tab. Order is enforced server-side too; the screen
just makes it legible. Captures are compressed hard on purpose: matching needs
resolution around the face, not a printable image, and a small payload uploads
far more reliably from a phone in somebody's car. Oversized captures are
caught on the device so a caregiver sees a retake prompt instead of a 413.

Two things the screen says out loud rather than burying. A failed match never
implies fraud: the likeliest cause is lighting or an angle, and an app is the
wrong place to make that call. And because liveness is not built, the screen
states that the check does not detect a photo of a screen or a printed
picture. Withdrawing consent is one tap from the same screen, and a failed
deletion says so rather than claiming the photo is gone.

expo-camera runs in Expo Go on SDK 54, so this is testable on a phone without
a custom native build. Liveness is what would have needed one.
@SishirP17
SishirP17 requested a review from durga710 as a code owner August 5, 2026 00:27
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rayhealth-evv-platform-app Ready Ready Preview Aug 5, 2026 12:28am

@SishirP17
SishirP17 merged commit 60f9295 into main Aug 5, 2026
14 checks passed
@SishirP17
SishirP17 deleted the feat/identity-capture-ui branch August 5, 2026 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant