Skip to content

MS-1491 Spoof check implementation#1735

Open
luhmirin-s wants to merge 13 commits into
mainfrom
feature/MS-1491-spoof-check-ux
Open

MS-1491 Spoof check implementation#1735
luhmirin-s wants to merge 13 commits into
mainfrom
feature/MS-1491-spoof-check-ux

Conversation

@luhmirin-s

Copy link
Copy Markdown
Contributor

JIRA ticket
Will be released in: 2026.3.0

Design document

Notable changes

  • Added spoof checks when using ROC v3 face SDK (other SDKs have no-op stubs). Spoof check return the "probability that provided image is being spoofed" and must not be over the configured threshold. If any of the images are over the threshold and the mode is configured to be "ENFORCED", the capture is reset up to configured amount of times.
Screen.Recording.2026-07-07.at.15.59.36.mov
  • In "RECORDED" mode the spoof check is performed and results are stored, but the flow is not reset in case of failed check.

Custom config

Will be added to the documentation before merging this PR.

"spoofCheckMode": "ENFORCED",  // Or "DISABLED", or "RECORDED"
"spoofCheckThreshold": 0.33,  // Probability cutoff in (0; 1) range
"spoofMaxAttempts": 2,  // How many attempts are users forced to do until all images pass
"spoofMaxBitmapSize": 1500,  // Largest image size to scale down to before check to ensure IOD is within range
"spoofMinValidationUiDurationMs": 2000,  // At least how long the processing is shown
"spoofMinValidationErrorUiDurationMs": 2000,  // At least how long error is shown

Testing guidance

  • Configure a project with face modality, ROC v3 and 2-5 samples to capture.
  • Add the custom configuration "spoofCheckMode": "ENFORCED" and try to run identification flows:
    • with printed/screen faces - it should force re-capture;
    • with real faces - it should proceed without re-capture.
  • Add the custom configuration "spoofCheckMode": "RECORDED" and try to run identification flows:
    • it should proceed without re-capture on any face;
    • spoof score is recorded in the session events (either in troubleshooting screen or network data).
  • Currently the logging is a bit verbose to help with debugging, it will be cleared before merging the PR.

Additional work checklist

  • Effect on other features and security has been considered
  • Design document marked as "In development" (if applicable)
  • External (Gitbook) and internal (Confluence) Documentation is up to date (or ticket created)
  • Test cases in Testiny are up to date (or ticket created)
  • Other teams notified about the changes (if applicable)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Implements face spoof-check support for ROC v3 captures, including configuration parsing, UI states for “validating”/“failed validation”, and propagation of spoof scores into capture events and API payload mappings.

Changes:

  • Adds a spoof-check API to FaceDetector with ROC v3 implementation (others return “not available”).
  • Introduces project custom-config parsing + use case to enable/disable spoof checks for ROC v3 and to configure thresholds/attempts/UI timings.
  • Extends face capture flow/UI and event payloads to record spoof score / skip reasons, with updated API mapping and tests.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
infra/resources/src/main/res/values/strings.xml Adds UI strings for validating and validation failure states.
infra/events/src/test/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureEventTest.kt Updates event test data to include spoof fields and named args.
infra/events/src/main/java/com/simprints/infra/events/event/domain/models/FaceCaptureEvent.kt Adds spoof fields to payload face model + skip-reason enum.
infra/events/src/debug/java/com/simprints/infra/events/sampledata/EventFactoryUtils.kt Updates debug sample event factory to include spoof fields.
infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/face/ApiFaceCapturePayloadTest.kt Extends API mapping tests for spoof fields + refactors result mapping tests.
infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/EventValidationUtils.kt Updates JSON validation expectations for spoof fields in face capture payloads.
infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/ApiFaceCapturePayload.kt Adds spoof fields + skip-reason enum to API payload model and mapping.
infra/config-store/src/test/java/com/simprints/infra/config/store/models/ExperimentalProjectConfigurationTest.kt Adds tests for spoof-check custom config parsing defaults/validation.
infra/config-store/src/main/java/com/simprints/infra/config/store/models/FaceConfiguration.kt Introduces spoof-check configuration and mode enum.
infra/config-store/src/main/java/com/simprints/infra/config/store/models/ExperimentalProjectConfiguration.kt Adds spoof-check custom config parsing with defaults and coercion.
face/infra/simface/src/main/java/com/simprints/face/infra/simface/detection/SimFaceDetector.kt Adds no-op spoofCheck() stub returning NOT_AVAILABLE.
face/infra/roc-v3/src/main/java/com/simprints/face/infra/rocv3/detection/RocV3Detector.kt Implements ROC v3 spoof check (scaling, landmarks/IOD gating, score computation).
face/infra/roc-v1/src/main/java/com/simprints/face/infra/rocv1/detection/RocV1Detector.kt Adds no-op spoofCheck() stub returning NOT_AVAILABLE.
face/infra/base-bio-sdk/src/main/java/com/simprints/face/infra/basebiosdk/detection/SpoofCheckResult.kt Adds shared model for spoof score + optional skip reason.
face/infra/base-bio-sdk/src/main/java/com/simprints/face/infra/basebiosdk/detection/FaceDetector.kt Expands detector interface to include spoofCheck().
face/capture/src/test/java/com/simprints/face/capture/usecases/SimpleCaptureEventReporterTest.kt Adds tests for mapping spoof skip reasons into capture events.
face/capture/src/test/java/com/simprints/face/capture/usecases/GetSpoofCheckConfigurationUseCaseTest.kt Adds unit tests for enabling spoof-check config only for ROC v3.
face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModelTest.kt Adds validation/reset behavior tests for recorded/enforced spoof modes.
face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackAutoCaptureFragmentViewModelTest.kt Adds auto-capture validation behavior tests and updates process() signature usage.
face/capture/src/test/java/com/simprints/face/capture/screens/livefeedback/CropToTargetOverlayAnalyzerTest.kt Updates analyzer callback signature to include original + cropped bitmaps.
face/capture/src/main/java/com/simprints/face/capture/usecases/SimpleCaptureEventReporter.kt Records spoof score/skip reason into FaceCaptureEvent payloads.
face/capture/src/main/java/com/simprints/face/capture/usecases/GetSpoofCheckConfigurationUseCase.kt Introduces use case to derive spoof-check config gated on ROC v3.
face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragmentViewModel.kt Adds validating/failed states, runs spoof checks, and enforces recapture attempts.
face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/LiveFeedbackFragment.kt Renders validating/failed UI and wires progress updates + analyzer callback changes.
face/capture/src/main/java/com/simprints/face/capture/screens/livefeedback/CropToTargetOverlayAnalyzer.kt Now returns both original and cropped bitmaps for spoof check + detection.
face/capture/src/main/java/com/simprints/face/capture/models/FaceDetection.kt Stores original bitmap + spoofCheckResult on detections.

@luhmirin-s luhmirin-s force-pushed the feature/MS-1491-spoof-check-ux branch from bbb82fe to ea62fcd Compare July 7, 2026 14:02
@luhmirin-s luhmirin-s requested review from a team, BurningAXE, TristramN, alex-vt, alexandr-simprints, meladRaouf and ybourgery and removed request for a team July 7, 2026 14:08
@luhmirin-s luhmirin-s marked this pull request as ready for review July 8, 2026 07:03

@Keep
@Serializable
enum class Result {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@luhmirin-s Don't you need to add the SPOOFED result too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That is a good question, it was obvious in the initial version of the document where spoof check was part of the feedback loop. Spoof detection is now fully separate process and these result values are taken directly from the feedback loop statuses.

We could add the value and assign it after the check, but I think it might be unnecessary since the score is already present in the face data,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

But if the face is spoofed, it means that we would not use the FaceCapture and SID will not extract the template for it no?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

And in this case what would be the result in the FaceCapture, VALID even if it's not valid?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Since the spoof check is a computation heavy operation (it takes tens of ms to extract template and couple hundred to spoof check), it is done after all "valid" captures are gathered. So the capture can be "valid" (there is a face in the right position), but still have a high probability of being spoofed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's chat only in the design doc so that it would be easier for other people to look at it comment on it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added the value. Only marking the captures in the event just before saving it to the database, so that it does not interfere with existing UI logic.

@sonarqubecloud

sonarqubecloud Bot commented Jul 8, 2026

Copy link
Copy Markdown

@alexandr-simprints

alexandr-simprints commented Jul 9, 2026

Copy link
Copy Markdown
Contributor
image

I know it might be subjective, but in my opinion, the 'Unable to verify' state is unclear:

  • The color scheme does not differ from the 'Scanning phase'
  • There might not be enough time to read the text in the field setting
  • Lack of required user input. Automation is good but the 'Unable to verify' state is displayed for a static amount of time, and we cannot be sure whether it's too short or too long

My suggestion would be to add a popup bottom sheet dialog when the 'Unable to verify' state is reached. Considering that the spoof positives should be an edge case, I think it's best to give the user enough time to familiarize with the message, and confirm their intent to recapture

@luhmirin-s

Copy link
Copy Markdown
Contributor Author
  • The color scheme does not differ from the 'Scanning phase'
  • There might not be enough time to read the text in the field setting
    My suggestion would be to add a popup bottom sheet dialog when the 'Unable to verify' state is reached. Considering that the spoof positives should be an edge case, I think it's best to give the user enough time to familiarize with the message, and confirm their intent to recapture

I discussed this with Jon and we decided that the current state is "good enough" for a first version as we are not sure how prevalent this state will be how disruptive it should be. It is very likely that "enforced" mode would not be used in this version to gather some real-life data first.

  • Lack of required user input. Automation is good but the 'Unable to verify' state is displayed for a static amount of time, and we cannot be sure whether it's too short or too long

There are custom config fields to adjust UI timing specifically for this reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants