Skip to content

feat: MRZ data confirmation for NFC scanning#1767

Merged
transphorm merged 13 commits intodevfrom
shazarre/feat/mrz_data_confirmation
Apr 22, 2026
Merged

feat: MRZ data confirmation for NFC scanning#1767
transphorm merged 13 commits intodevfrom
shazarre/feat/mrz_data_confirmation

Conversation

@shazarre
Copy link
Copy Markdown
Collaborator

@shazarre shazarre commented Feb 17, 2026

Description

This PR adds support for confirming MRZ data needed for later NFC scanning utilizing euclid DataConfirmation screen.

Tested

Tested on an example MRZ data (https://www.idenfy.com/blog/machine-readable-zone/) and a real passport.

Screenshot_20260421_120016 Screenshot_20260421_120035

How to QA

Scan MRZ data, compare with the result on the screen (change something and observe analytics data).

Blocked by https://github.com/selfxyz/euclid/pull/71

TODO

  • test on iOS

Summary by CodeRabbit

  • New Features

    • Date-capable input component (alphanumeric + date modes) with in-app date picker.
    • Data Confirmation screen to review/edit extracted document fields; navigation updated accordingly.
    • SDK: MRZ date-parsing helpers exposed and new analytics events for data confirmation and MRZ modifications.
  • Tests

    • Added tests for Data Confirmation flows, MRZ diffs, and MRZ date parsing.
  • Chores

    • Added react-native-date-picker dependency and enabled its transformation in tests.

@shazarre shazarre self-assigned this Feb 17, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 17, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fb1c304d-a77f-4d0d-a239-4b79bca7acdf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a DataConfirmation screen and InputField (with date picker), MRZ date-parsing helpers and re-exports in the SDK, new analytics events, navigation wiring to DataConfirmation, utilities/tests for string diffing, and the react-native-date-picker dependency.

Changes

Cohort / File(s) Summary
Input Component & Dependency
app/package.json, app/src/components/InputField.tsx
Added react-native-date-picker dependency and a new InputField component supporting alphanumeric and date modes, with sanitization and date-picker modal.
Navigation & Event Routing
app/src/navigation/documents.ts, app/src/navigation/types.ts, app/src/providers/selfClientProvider.tsx, app/tests/src/navigation.test.tsx
Registered DataConfirmation route and added to route types/tests; changed DOCUMENT_MRZ_READ_SUCCESS navigation target to DataConfirmation.
Data Confirmation Feature & Tests
app/src/screens/documents/scanning/DataConfirmationScreen.tsx, app/src/screens/documents/scanning/DataConfirmationScreen.test.tsx
New DataConfirmationScreen rendering MRZ fields, computing per-field first-differences, tracking analytics (including MRZ_DATA_MODIFIED / DATA_CONFIRMATION_COMPLETED), updating MRZ store for NFC, and navigating to NFC scan; comprehensive tests added.
Diff Utility & Tests
app/src/utils/diffCalculator.ts, app/src/utils/diffCalculator.test.ts
New utility calculateFirstDifference with unit tests covering substitutions, length diffs, and edge cases.
Document Attributes Refactor
app/src/utils/documentAttributes.ts
Consolidated getDocumentAttributes implementation (removed duplicate export).
SDK MRZ Parsing, Exports & Tests
packages/mobile-sdk-alpha/src/processing/mrz.ts, packages/mobile-sdk-alpha/src/mrz/index.ts, packages/mobile-sdk-alpha/src/index.ts, packages/mobile-sdk-alpha/tests/processing/mrz.test.ts, packages/mobile-sdk-alpha/src/constants/analytics.ts
Added MRZ date parsing helpers (parseMRZBirthDate, parseMRZExpiryDate) and re-exported them in SDK public API; added analytics event constants; extended SDK tests for date parsing edge cases.
Jest Config
app/jest.config.cjs
Added react-native-date-picker to transform allowlist so Jest/Babel will transform it.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant DataConfirmation as "DataConfirmationScreen"
    participant InputField as "InputField"
    participant MRZStore as "MRZ Store"
    participant Analytics as "Analytics"
    participant Navigation as "Navigation"

    User->>DataConfirmation: open (after MRZ read)
    DataConfirmation->>MRZStore: read MRZ data
    MRZStore-->>DataConfirmation: initial fields
    User->>InputField: edit fields (number / dates)
    InputField-->>DataConfirmation: onChangeText / onDateChange
    User->>DataConfirmation: tap Continue
    DataConfirmation->>DataConfirmation: calculateFirstDifference(original, modified)
    alt changes detected
        DataConfirmation->>Analytics: track MRZ_DATA_MODIFIED
        DataConfirmation->>MRZStore: setMRZForNFC(updatedData)
    else no changes
        DataConfirmation->>Analytics: track DATA_CONFIRMATION_COMPLETED (had_changes: false)
    end
    DataConfirmation->>Navigation: navigate to DocumentNFCScan
    Navigation-->>User: show NFC scan
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • transphorm
  • seshanthS

Poem

✨ A passport whisper, letters bright,
Dates aligned beneath the light,
Diffs discovered, logs compose,
Analytics hum — the flow now goes,
NFC waits — the app takes flight. 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main objective: adding MRZ data confirmation functionality for NFC scanning.
Description check ✅ Passed The PR description provides a clear summary of changes, testing details with screenshots, QA instructions, and identifies a blocking dependency.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch shazarre/feat/mrz_data_confirmation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@shazarre
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented Mar 10, 2026

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@shazarre shazarre marked this pull request as ready for review March 10, 2026 16:44
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/components/InputField.tsx`:
- Around line 164-175: The shared text style object textInput in InputField.tsx
includes the web-only property outlineStyle which is not part of React Native's
TextStyle and causes TS2769; remove outlineStyle from the textInput style (or
move it behind a web-only conditional) and ensure any web-specific focus styling
is applied only in a platform-specific path (e.g., a separate web-only style or
Platform.select) so TextInput and its typing accept the style without error.

In `@app/src/screens/documents/scanning/DataConfirmationScreen.test.tsx`:
- Around line 55-57: The test currently creates a nested require('react-native')
inside the jest.mock callback which is banned; fix by removing the nested
require and instead import View and Text at the top of the test file via
top-level ES imports (e.g., import { View, Text } from 'react-native') and then
use those imported symbols inside the jest.mock('@/components/InputField', ...)
mock implementation so the mock reuses the hoisted View and Text instead of
requiring react-native at runtime.
- Around line 55-77: The current jest.mock for InputField replaces the real
component with a passive stub that doesn't forward edits, hiding the MRZ
confirmation behavior; replace the stub with a minimal mock that preserves
behavior by rendering a TextInput (or a component that calls the provided
onChangeText prop) and binds value and onChangeText so tests can drive and
observe actual user edits. Locate the mocked InputField in
DataConfirmationScreen.test.tsx (the jest.mock block) and change it to render an
interactive element that uses the onChangeText prop (and the same testID scheme)
or simply import the real InputField component for the test so
fireEvent.changeText triggers the same prop wiring the production code uses.
Ensure inputFieldCallbacks are no longer used to simulate edits; instead drive
edits via fireEvent.changeText on the rendered testID to validate real prop
forwarding.

In `@app/src/screens/documents/scanning/DataConfirmationScreen.tsx`:
- Around line 73-78: handleDateChange currently calls
formatDateToYYMMDD(date.toISOString()) which shifts the day for non-UTC users;
instead generate a local-date ISO (using date.getFullYear(), date.getMonth()+1,
date.getDate() with zero-padding) or otherwise convert the Date to a local
YYYY-MM-DD string and pass that into formatDateToYYMMDD before calling
setFields, so the MRZ YYMMDD preserves the user's selected local day while
InputField can still display its selectedDate. Ensure you update only
handleDateChange (and keep formatDateToYYMMDD usage) so setFields([... ,
[field]: mrzFormattedDate]) receives the corrected local-based MRZ value.
- Around line 89-121: The code currently sends raw MRZ characters because
calculateFirstDifference(...) results are placed into diffs and forwarded to
trackEvent(PassportEvents.MRZ_DATA_MODIFIED); replace that behavior by redacting
or replacing the diff values before sending (e.g., store only field names,
indices/positions of differences, masked snippets like first/last character or
length, or a boolean), i.e., transform filteredDiffs so values do NOT contain
actual MRZ/PII (use calculateFirstDifference only to compute metadata, then map
its output to a non-PII representation) and pass that sanitized object and
counts to trackEvent instead of raw diffs.

In `@packages/mobile-sdk-alpha/src/processing/mrz.ts`:
- Around line 341-350: parseMRZDateComponents currently only checks length and
allows non-numeric/impossible YYMMDD values through; update it to reject
malformed dates by (1) ensuring all 6 characters are digits, (2) parsing
year/month/day into numbers, (3) mapping two‑digit year to a full year the code
expects (keep existing YY->YYYY logic if present elsewhere), (4) constructing a
Date (use UTC or consistent timezone) and then verifying the Date's
year/month/day match the parsed components (to catch overflow like month=13 or
day=32); if any check fails return null. Apply the same stricter
validation/verification logic to parseMRZExpiryDate so both helpers only return
valid date objects or null.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1a2045ba-c697-4fe5-8dc1-30f825b4bb32

📥 Commits

Reviewing files that changed from the base of the PR and between d7c1556 and 737679d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • app/package.json
  • app/src/components/InputField.tsx
  • app/src/navigation/documents.ts
  • app/src/navigation/types.ts
  • app/src/providers/selfClientProvider.tsx
  • app/src/screens/documents/scanning/DataConfirmationScreen.test.tsx
  • app/src/screens/documents/scanning/DataConfirmationScreen.tsx
  • app/src/utils/diffCalculator.test.ts
  • app/src/utils/diffCalculator.ts
  • app/src/utils/documentAttributes.ts
  • packages/mobile-sdk-alpha/src/constants/analytics.ts
  • packages/mobile-sdk-alpha/src/index.ts
  • packages/mobile-sdk-alpha/src/mrz/index.ts
  • packages/mobile-sdk-alpha/src/processing/mrz.ts
  • packages/mobile-sdk-alpha/tests/processing/mrz.test.ts

Comment thread app/src/components/InputField.tsx
Comment thread app/src/screens/documents/scanning/DataConfirmationScreen.test.tsx Outdated
Comment thread app/src/screens/documents/scanning/DataConfirmationScreen.test.tsx Outdated
Comment thread app/src/screens/documents/scanning/DataConfirmationScreen.tsx Outdated
Comment thread app/src/screens/documents/scanning/DataConfirmationScreen.tsx Outdated
Comment thread packages/mobile-sdk-alpha/src/processing/mrz.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/components/InputField.tsx`:
- Around line 14-15: The file imports the native DatePicker unconditionally and
renders it inside the InputField component, which breaks web; change to a
platform split by removing the top-level import of DatePicker and instead
require/import the native picker only on native platforms (use Platform.OS or
Platform.select) and render a web-safe fallback (e.g., an HTML input type="date"
or react-native-web compatible component) inside InputField's render/return
path; specifically, replace the unconditional DatePicker import with a
conditional lazy require (or dynamic import) and update the JSX in InputField
(the block that currently renders DatePicker) to choose between the native
DatePicker and the web input based on Platform.OS === 'web'.
- Around line 42-44: InputField currently constructs selectedDate once with
useState and formats it using local getters which causes timezone shifts, and it
imports react-native-date-picker unconditionally breaking web builds; update
InputField to (1) resync selectedDate whenever the value prop changes by adding
a useEffect that calls setSelectedDate(new Date(value)) (ensure you
normalize/parse the incoming ISO as UTC if needed), (2) when producing display
strings or extracting year/month/day use UTC getters
(getUTCFullYear/getUTCMonth/getUTCDate) instead of local getters to avoid
midnight-UTC shifts, and (3) make the react-native-date-picker import/platform
usage conditional (use Platform.OS !== 'web' or a dynamic require) and render a
web-safe fallback (e.g., input type="date" or a web picker) when on web so the
native-only library is not imported on web. Ensure references to selectedDate,
setSelectedDate, InputField, and the date formatting code are updated
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8a061e26-5b2d-40fa-b198-3664fc47381f

📥 Commits

Reviewing files that changed from the base of the PR and between 737679d and f3e3126.

📒 Files selected for processing (1)
  • app/src/components/InputField.tsx

Comment thread app/src/components/InputField.tsx
Comment thread app/src/components/InputField.tsx Outdated
@shazarre shazarre force-pushed the shazarre/feat/mrz_data_confirmation branch from 02e88c1 to 45c89fe Compare April 21, 2026 09:55
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 21, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
self-webview-app Ignored Ignored Preview Apr 22, 2026 10:27pm

Request Review

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 21, 2026

Greptile Summary

This PR introduces an MRZ data confirmation screen that lets users review and correct passport/ID fields extracted via camera scanning before proceeding to NFC chip reading. It adds a new DataConfirmation route, a dual-mode InputField component (alphanumeric text + native date picker), YYMMDD display/conversion utilities, two new MRZ date-parsing helpers exported from mobile-sdk-alpha, and a "Check scanned data" escape hatch on the RegistrationFallbackNFCScreen.

Key observations:

  • The DataConfirmation screen correctly reads from the MRZ Zustand store, initialises local state, calls setMRZForNFC only when the user has actually changed a field, and fires the DATA_CONFIRMATION_COMPLETED analytics event — previously flagged concerns about spreading the store object have been addressed.
  • Year-century logic is consistent: both birthDateToDisplay and parseMRZBirthDate use a 30-year cutoff (≤30 → 2000s, >30 → 1900s); expiry always assumes 2000+year.
  • A debug console.log in validateTD1Format logs raw MRZ lines (which contain DOB, document number, nationality, expiry) to the console — this should be removed before the branch ships.
  • PassportEvents.MRZ_DATA_MODIFIED is declared in analytics constants but not tracked anywhere; it appears to be a leftover from the removed diff-sending logic.
  • The InputField date picker falls back to new Date() (today) when the YYMMDD value is empty, resulting in an unintuitive initial date for the birth date field.

Confidence Score: 4/5

Safe to merge after removing the console.log that leaks MRZ data to device logs.

The feature is well-scoped and the previously flagged store-spreading issue has been resolved. The one blocking item is the debug console.log in validateTD1Format which logs raw passport PII; removing that single line is a trivial fix. The remaining findings (empty-value DatePicker default, unused analytics event) are non-blocking P2 cleanups.

packages/mobile-sdk-alpha/src/processing/mrz.ts — remove the console.log on line 65 before shipping.

Security Review

  • Sensitive data logging (packages/mobile-sdk-alpha/src/processing/mrz.ts:65): validateTD1Format logs raw MRZ lines to the console. These lines contain PII — document number, date of birth, nationality, sex, expiry date. In production builds this data may be captured by crash-reporting SDKs, MDM logging, or local adb/idevicesyslog sessions. The log should be removed.

Important Files Changed

Filename Overview
app/src/screens/documents/scanning/DataConfirmationScreen.tsx New screen for reviewing/editing extracted MRZ fields before NFC scanning; cleanly reads from MRZ store, tracks analytics, and calls setMRZForNFC only when data has changed.
app/src/components/InputField.tsx New multi-mode input component supporting alphanumeric text and date picker modes; DatePicker fallback defaults to today's date when value is empty, which is an odd UX default for a birth date field.
packages/mobile-sdk-alpha/src/processing/mrz.ts Added parseMRZBirthDate / parseMRZExpiryDate helpers; contains a leftover debug console.log in validateTD1Format that logs raw MRZ lines containing sensitive personal data.
app/src/utils/yymmdd.ts New YYMMDD display/conversion utilities with consistent year-century cutoff (≤30 → 2000s) matching parseMRZBirthDate; well-covered by tests.
app/src/screens/documents/scanning/RegistrationFallbackNFCScreen.tsx Added 'Check scanned data' button navigating to the new DataConfirmation screen, giving users an escape hatch when NFC scanning fails.
packages/mobile-sdk-alpha/src/constants/analytics.ts Added DATA_CONFIRMATION_COMPLETED and MRZ_DATA_MODIFIED events; MRZ_DATA_MODIFIED is defined but not tracked anywhere in the codebase.
packages/mobile-sdk-alpha/src/mrz/index.ts Re-exports newly added parseMRZBirthDate and parseMRZExpiryDate from the processing layer for external consumption.
app/src/navigation/documents.ts Registers new DataConfirmation screen with HeadlessNavForEuclid header and correct statusBar wiring from the screen's static property.

Comments Outside Diff (1)

  1. packages/mobile-sdk-alpha/src/processing/mrz.ts, line 64-65 (link)

    P1 security Debug console.log exposes sensitive passport data

    validateTD1Format logs the raw MRZ lines to the console before any validation. MRZ lines contain personally identifiable information: document number, date of birth, nationality, sex, and expiry date. In a production build this data will appear in device logs, which can be collected by crash reporters, MDM solutions, or local debugging tools.

Reviews (2): Last reviewed commit: "simplify date handling" | Re-trigger Greptile

Comment thread app/src/screens/documents/scanning/DataConfirmationScreen.tsx
Comment thread app/src/utils/diffCalculator.ts
Comment thread app/src/components/InputField.tsx Outdated
@transphorm transphorm merged commit 94e2b4d into dev Apr 22, 2026
43 checks passed
@transphorm transphorm deleted the shazarre/feat/mrz_data_confirmation branch April 22, 2026 22:52
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.

2 participants