Skip to content

feat/face-compare-api-addition#1331

Merged
RitikaHanish merged 1 commit into
mainfrom
feat/face-compare-api-addition
Jun 11, 2026
Merged

feat/face-compare-api-addition#1331
RitikaHanish merged 1 commit into
mainfrom
feat/face-compare-api-addition

Conversation

@RahulGupta0706

@RahulGupta0706 RahulGupta0706 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates

Description

Additional Changes

  • This PR modifies the database schema (database migration added)
  • This PR modifies dhall configs/environment variables

Motivation and Context

How did you test it?

Checklist

  • I formatted the code and addressed linter errors ./dev/format-all-files.sh
  • I reviewed submitted code
  • I added unit tests for my changes where possible
  • I added a CHANGELOG entry if applicable

Summary by CodeRabbit

  • New Features

    • Added synchronous face-compare for document↔selfie and document↔document checks; responses include match score, per-image details and a review recommendation, exposed via the verification interface and implemented for the Idfy provider.
  • Configuration

    • Added configurable retry limit for face-compare; system will retry per configuration and surface a user-facing error if retries are exhausted.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

A new face comparison endpoint is introduced across the Idfy client library and verification interface. Request and response types are defined at both the Idfy provider level and the generic interface level. The Idfy client implements the HTTP call via Servant. An adapter bridges interface types to the Idfy client, and a dispatcher routes requests to Idfy or returns "Not Implemented" for other providers.

Changes

Face Comparison Feature

Layer / File(s) Summary
Type definitions and data schemas
lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs, lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs, lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
Interface DTO types FaceCompareReq and FaceCompareResp wrap document pairs and optional Idfy response data. Idfy request type FaceCompareRequestBody carries two document identifiers. Idfy response types FaceCompareResponseData and FaceImageResult model face detection results, match scores, and recommendation flags with JSON/schema derivations.
Idfy HTTP client and Servant API
lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
Servant API type FaceCompareAPI routes to the compare endpoint. Client function faceCompare sends FaceCompareRequest via shared callIdfyAPI helper and returns FaceCompareResponse.
Interface Idfy adapter function
lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs
Adapter faceCompare constructs an Idfy request from input documents, decrypts API credentials, calls the Idfy client, and wraps the result into FaceCompareResp format, retrying per IdfyCfg.faceCompareRetryLimit when needed.
Provider dispatch and routing
lib/mobility-core/src/Kernel/External/Verification/Interface.hs
Router function faceCompare dispatches requests to Idfy.faceCompare when IdfyConfig is present and returns InternalError "Not Implemented!" for other provider types (GovtDataConfig, FaceVerificationConfig, HyperVerge*, DigiLockerConfig, TtenVerificationConfig, MorthConfig).
Idfy configuration
lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs
Adds faceCompareRetryLimit :: Maybe Int to IdfyCfg to control retry attempts in the adapter.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant IdfyClient
  participant IdfyService
  Client->>IdfyClient: faceCompare(FaceCompareRequest)
  IdfyClient->>IdfyService: POST /v3/tasks/sync/compare/face
  IdfyService-->>IdfyClient: FaceCompareResponse
  IdfyClient-->>Client: FaceCompareResponse
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • RitikaHanish

Poem

🐰 I hopped through types and client land,
I stitched requests with steady hand,
Idfy calls and retries bright,
face-compare bound, the flow takes flight,
🥕✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat/face-compare-api-addition' accurately describes the main change: adding a new face comparison API endpoint across the Idfy verification service integration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 feat/face-compare-api-addition

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.

@RahulGupta0706

Copy link
Copy Markdown
Contributor Author

@ClaudeWeb please review

@khuzema786

Copy link
Copy Markdown
Contributor

I'll fetch the PR details, diff, and run a security scan in parallel.Let me try using the GitHub MCP tools instead.Now let me get the changed files and look at the diff.Now let me look at the surrounding context in the existing source files to compare patterns.Now let me check the existing Idfy request/response types and the Interface/Idfy.hs for patterns around document1/document2 field types.Let me check the Response.hs export list and the existing NameCompare response pattern for completeness.I have a complete picture of all 5 changed files and the full codebase context. Here is the review.


PR Review: feat/face-compare-api-addition

Verdict: Approve with minor concerns. The implementation is structurally correct and faithfully follows the established nameCompare pattern. No bugs, no security issues. A couple of design points worth raising.


Warning

1. PR description is completely empty (confidence: 92)

None of the checkboxes are checked, no description of what is being compared or why, no testing methodology. Given that this is a new biometric API integration involving face images, the PR template fields (motivation, test evidence) are important for reviewers and future git archaeology.


Suggestion

2. FaceCompareReq leaks the Idfy field names through the interface (confidence: 68)

File: Interface/Types.hs

data FaceCompareReq = FaceCompareReq
  { document1 :: Text,   -- what is this? selfie? license face crop?
    document2 :: Text,
    driverId  :: Text
  }

Compare with NameCompareReq, where the interface type uses domain-meaningful names that are then mapped to Idfy's generic field names:

data NameCompareReq = NameCompareReq
  { extractedName :: Text,   -- meaningful
    verifiedName  :: Text,   -- meaningful
    ...
  }
-- mapped in Interface/Idfy.hs to name1 / name2

document1/document2 are opaque — callers won't know which image is the selfie and which is the document face. Suggested fix:

data FaceCompareReq = FaceCompareReq
  { selfieImage   :: Text,  -- base64-encoded selfie
    documentImage :: Text,  -- base64-encoded face crop from ID document
    driverId      :: Text
  }

Then map in Interface/Idfy.hs:

Idfy.FaceCompareRequestBody
  { document1 = req.selfieImage,
    document2 = req.documentImage
  }

3. FaceCompareResp exposes a vendor-specific type at the interface boundary (confidence: 65)

File: Interface/Types.hs

newtype FaceCompareResp = FaceCompareResp
  { faceComparedData :: Maybe Idfy.FaceCompareResponseData
  }

This is the same pattern used by NameCompareResp and is a pre-existing design choice, so it's consistent. However, as more vendor-specific types accumulate in the interface layer, migrating to another provider (e.g. HyperVerge for face compare) would require a breaking interface change. Worth tracking if multi-provider face compare is ever planned.


Code Quality: No issues found

  • The Servant API route (/v3/tasks/sync/compare/face) and HTTP method (Post) match the Idfy sync compare endpoint.
  • FaceCompareResponseData and FaceImageResult are correctly typed with Maybe fields throughout — consistent with how all other Idfy response types handle optional fields.
  • Response.hs uses an open export (module ... where), so FaceCompareResponseData and FaceImageResult are automatically exported — no missing exports.
  • The callIdfyAPI invocation pattern in Client.hs is identical to nameCompare — correct.
  • All throwError $ InternalError "Not Implemented!" cases in Interface.hs for non-Idfy providers are consistent with every other function in that file.

@RahulGupta0706

Copy link
Copy Markdown
Contributor Author

Checked ClaudeWeb comments . nothing critical

@RahulGupta0706
RahulGupta0706 force-pushed the feat/face-compare-api-addition branch from c35c0ce to 3caec28 Compare June 5, 2026 10:17
@RitikaHanish

Copy link
Copy Markdown
Member

@ClaudeWeb Can you review, if errors are handled properly ?

@khuzema786

Copy link
Copy Markdown
Contributor

Let me do a focused error handling review across all changed files.Let me fetch callIdfyAPI and the nameCompare equivalent to compare error handling patterns.Now I have everything needed. Here is the focused error handling review.


Error Handling Review — feat/face-compare-api-addition

How the call chain works

callIdfyAPI
  └── callApiUnwrappingApiError (identity @IdfyError)
        → HTTP 4xx/5xx  : throws IdfyError         ✓
        → network error : throws exception          ✓
        → JSON parse    : throws exception          ✓
        → HTTP 200 with status="failed", result=Nothing : returns normally ✗

callIdfyAPI handles transport-level failures cleanly. The gap is entirely at the application level inside Interface/Idfy.hs.


Critical — Silent failure when Idfy reports a processing error (confidence: 92)

File: lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs

This is a synchronous API (/v3/tasks/sync/compare/face). When Idfy processing fails (e.g. unreadable image, internal error), it still returns HTTP 200 but with status = "failed" and result = null. The current code passes that through silently:

resp <- Idfy.faceCompare apiKey accountId url idfyReq
pure
  FaceCompareResp
    { faceComparedData = resp.result   -- Nothing on Idfy processing failure
    }

The caller receives FaceCompareResp { faceComparedData = Nothing } with no signal that the comparison actually failed. For a biometric identity-verification call, this is a real risk — upstream code may interpret Nothing as "comparison not available, proceed anyway" rather than "hard error."

Compare with getTask, the only place in this file that correctly guards against a missing result:

-- getTask — the right pattern
result <- resp.result & fromMaybeM (InternalError ("Missing result in getTask response: " <> show resp))

Suggested fix for Interface/Idfy.hs:

faceCompare cfg req = do
  let url = cfg.url
  apiKey    <- decrypt cfg.apiKey
  accountId <- decrypt cfg.accountId
  let reqData = Idfy.FaceCompareRequestBody
        { document1 = req.document1
        , document2 = req.document2
        }
  idfyReq  <- buildIdfyRequest req.driverId reqData
  resp     <- Idfy.faceCompare apiKey accountId url idfyReq
  result   <- resp.result
               & fromMaybeM (InternalError $ "faceCompare: missing result, status=" <> resp.status)
  pure FaceCompareResp { faceComparedData = Just result }

Note: nameCompare has the same gap. It's a pre-existing issue, but face comparison carries higher risk because it is a binary identity-gate — a silent Nothing is not an acceptable ambiguous state there either.


Warning — face_detected = False is not guarded at the interface layer (confidence: 78)

File: lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs

data FaceImageResult = FaceImageResult
  { face_detected :: Maybe Bool
  , face_quality  :: Maybe Text
  }

When Idfy cannot detect a face in either image, face_detected comes back Just False (or Nothing). In that case, is_a_match and match_score in FaceCompareResponseData are unreliable. The current code returns this data to callers without any defensive check.

This is lower-severity because callers can check face_detected themselves, but a guard here would make misuse much harder:

-- in Interface/Idfy.hs, after obtaining `result`
let faceNotDetected = 
      result.image_1 >>= (.face_detected) == Just False ||
      result.image_2 >>= (.face_detected) == Just False
when faceNotDetected $
  throwError $ InternalError "faceCompare: face not detected in one or both images"

No issues found in

Layer Verdict
Client.hs HTTP transport errors callApiUnwrappingApiError handles 4xx/5xx/network
decrypt cfg.apiKey / cfg.accountId ✓ throws on decryption failure
buildIdfyRequest (GUID gen) ✓ monadic, no silent failure path
Interface.hs dispatcher throwError "Not Implemented!" ✓ non-Idfy configs fail loudly
JSON decode of FaceCompareResponseData ✓ all fields Maybe, Servant throws on parse failure

@RahulGupta0706
RahulGupta0706 force-pushed the feat/face-compare-api-addition branch 2 times, most recently from 0e06507 to a7dd9d8 Compare June 8, 2026 11:48

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs`:
- Line 26: IdfyCfg's faceCompareRetryLimit is currently Maybe Int with no
validation; update the decode/constructor to enforce a non-negative bounded
value (e.g., change the field to Maybe Natural/Word or validate in the FromJSON
instance for IdfyCfg) and apply a max cap constant (e.g.,
MAX_FACE_COMPARE_RETRIES) or reject out-of-range values, then ensure faceCompare
still uses retryLimit = fromMaybe 1 cfg.faceCompareRetryLimit but with the
validated/clamped value so negative or excessively large inputs are prevented;
modify the FromJSON for IdfyCfg (or a smart constructor) to perform this check
and return a parse error or clamp to the cap.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2886cb79-21cf-47f5-88de-12f63591df37

📥 Commits

Reviewing files that changed from the base of the PR and between 0e06507 and a7dd9d8.

📒 Files selected for processing (7)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
🚧 Files skipped from review as they are similar to previous changes (6)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs

secret :: EncryptedField 'AsEncrypted Text,
url :: BaseUrl
url :: BaseUrl,
faceCompareRetryLimit :: Maybe Int

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all uses of faceCompareRetryLimit and whether any non-negative/bounded validation exists.
rg -n -C3 '\bfaceCompareRetryLimit\b|fromMaybe\s+1\s+cfg\.faceCompareRetryLimit|callWithRetry\s*\(' \
  lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs \
  lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs

Repository: nammayatri/shared-kernel

Length of output: 2361


🏁 Script executed:

#!/bin/bash
# Inspect callWithRetry definition and any handling of retryLimit/attemptsLeft <= 0
rg -n "callWithRetry|attemptsLeft" lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs -n

Repository: nammayatri/shared-kernel

Length of output: 212


🏁 Script executed:

#!/bin/bash
# Show the surrounding implementation area for callWithRetry
sed -n '470,560p' lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs

Repository: nammayatri/shared-kernel

Length of output: 3378


🏁 Script executed:

#!/bin/bash
# Inspect Config.hs around the IdfyCfg definition and the faceCompareRetryLimit field
sed -n '1,120p' lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs

Repository: nammayatri/shared-kernel

Length of output: 1269


🏁 Script executed:

#!/bin/bash
# Search whole repo for any other uses/validation of faceCompareRetryLimit
rg -n "\bfaceCompareRetryLimit\b" -S .

Repository: nammayatri/shared-kernel

Length of output: 310


Constrain faceCompareRetryLimit to a non-negative, bounded value

IdfyCfg defines faceCompareRetryLimit :: Maybe Int with derived FromJSON (no validation), and the only consumer is retryLimit = fromMaybe 1 cfg.faceCompareRetryLimit in faceCompare. Negative values make attemptsLeft > 0 false immediately (no retries, early failure), while very large values can amplify external-call retries and latency. Add non-negative/bounded validation at the config decode boundary (e.g., Maybe Natural/Word plus a max cap) and/or clamp/explicitly reject out-of-range values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs` at line
26, IdfyCfg's faceCompareRetryLimit is currently Maybe Int with no validation;
update the decode/constructor to enforce a non-negative bounded value (e.g.,
change the field to Maybe Natural/Word or validate in the FromJSON instance for
IdfyCfg) and apply a max cap constant (e.g., MAX_FACE_COMPARE_RETRIES) or reject
out-of-range values, then ensure faceCompare still uses retryLimit = fromMaybe 1
cfg.faceCompareRetryLimit but with the validated/clamped value so negative or
excessively large inputs are prevented; modify the FromJSON for IdfyCfg (or a
smart constructor) to perform this check and return a parse error or clamp to
the cap.

@RahulGupta0706
RahulGupta0706 force-pushed the feat/face-compare-api-addition branch from a7dd9d8 to 06bb283 Compare June 9, 2026 14:30
@coderabbitai
coderabbitai Bot requested a review from RitikaHanish June 9, 2026 14:31

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs`:
- Around line 551-553: The code returns FaceCompareResp {faceComparedData = Just
result} for any Just result; update the case handling for resp.result to first
verify the face-detection flags on the returned result (the detection fields on
result) are present and true, and only then set faceComparedData to Just result;
if any detection flag is false or missing, throw/return InternalError instead of
returning faceComparedData. Ensure this validation is implemented where
resp.result is matched (the Just result branch) and that the error uses the
existing InternalError type/constructor used elsewhere in this module.
- Around line 548-555: The current retry loop regenerates idfyReq (and its
task_id) each attempt in callWithRetry by calling buildIdfyRequest(req.driverId
reqData) inside the recursion; change the logic to build idfyReq once before
starting retries and pass that same idfyReq into callWithRetry (or convert
callWithRetry to accept an idfyReq parameter) so Idfy.faceCompare always
receives the same payload; update references to idfyReq inside callWithRetry and
keep the same retry/decrement logic and error handling around Idfy.faceCompare
and the attemptsLeft parameter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cb64f86a-75d1-4562-a88c-c08cada6e8d3

📥 Commits

Reviewing files that changed from the base of the PR and between a7dd9d8 and 06bb283.

📒 Files selected for processing (7)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
🚧 Files skipped from review as they are similar to previous changes (6)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs

Comment on lines +551 to +553
case resp.result of
Just result -> pure FaceCompareResp {faceComparedData = Just result}
Nothing

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate success on face-detection validity before returning match data.

At Line 552, success is returned for any Just result, but face-compare outputs are unreliable when one or both images have no detected face. Please enforce a validation guard on the detection flags and throw InternalError when detection is false/missing instead of returning faceComparedData.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs` around
lines 551 - 553, The code returns FaceCompareResp {faceComparedData = Just
result} for any Just result; update the case handling for resp.result to first
verify the face-detection flags on the returned result (the detection fields on
result) are present and true, and only then set faceComparedData to Just result;
if any detection flag is false or missing, throw/return InternalError instead of
returning faceComparedData. Ensure this validation is implemented where
resp.result is matched (the Just result branch) and that the error uses the
existing InternalError type/constructor used elsewhere in this module.

deriving (Show, Generic, FromJSON, ToJSON, ToSchema)

data FaceCompareReq = FaceCompareReq
{ selfieImage :: Text,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can keep generic names. Order doesn't matter anyway

@coderabbitai coderabbitai Bot 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.

♻️ Duplicate comments (1)
lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs (1)

551-553: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Validate face detection before returning match data.

The code returns faceComparedData = Just result for any Just result without verifying that faces were actually detected in both images. According to the Idfy face-compare contract, when face_detected is False or missing for either image, the is_a_match and match_score fields are unreliable. Add a validation guard to check the face-detection flags on result and throw InternalError when detection fails, rather than returning potentially misleading biometric match data.

🛡️ Suggested validation logic
         case resp.result of
-          Just result -> pure FaceCompareResp {faceComparedData = Just result}
+          Just result -> do
+            -- Validate face detection before returning result
+            -- Access result.face_detected fields and verify both are True
+            -- If either is False/Nothing, throw InternalError
+            -- Example (adjust based on actual FaceCompareResponseData structure):
+            -- when (result.face_detected /= Just True) $
+            --   throwError $ InternalError "Face detection failed in one or both images."
+            pure FaceCompareResp {faceComparedData = Just result}
           Nothing

Note: You'll need to inspect the Idfy.FaceCompareResponseData structure to determine the exact field names for face detection flags.

As per coding guidelines and PR objectives, biometric APIs should validate detection success before returning match results.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs` around
lines 551 - 553, The current branch unconditionally returns FaceCompareResp
{faceComparedData = Just result} when resp.result is Just result; instead
validate detection flags on the Idfy.FaceCompareResponseData (inspect fields
like face_detected / faceDetection / per-image flags inside the result) before
returning: if either image's face-detected flag is False or missing, throw an
InternalError (use the same error type used elsewhere) rather than returning
match data; update the case in the function handling resp.result to perform this
guard and only set faceComparedData = Just result when detection succeeded for
both images.
🧹 Nitpick comments (2)
lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs (2)

349-353: ⚡ Quick win

Consider domain-meaningful field names to improve clarity.

The current field names documentImage1 and documentImage2 are vendor-opaque and don't communicate intent. Following the pattern of NameCompareReq (which uses extractedName and verifiedName), consider renaming these fields to convey their semantic roles—e.g., selfieImage and documentImage—and map them to the Idfy provider's document1/document2 fields in the adapter layer at Interface/Idfy.hs.

♻️ Suggested refactor
 data FaceCompareReq = FaceCompareReq
-  { documentImage1 :: Text,
-    documentImage2 :: Text,
+  { selfieImage :: Text,
+    documentImage :: Text,
     driverId :: Text
   }

Then update the adapter in Interface/Idfy.hs lines 544-546:

   let reqData =
         Idfy.FaceCompareRequestBody
-          { document1 = req.documentImage1,
-            document2 = req.documentImage2
+          { document1 = req.selfieImage,
+            document2 = req.documentImage
           }

As per coding guidelines, this aligns with the domain-driven naming pattern seen in NameCompareReq.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs` around
lines 349 - 353, Rename the vendor-opaque fields in FaceCompareReq to
domain-meaningful names (e.g., change documentImage1/documentImage2 to
selfieImage/documentImage or similar) and update any construction/consumption
sites accordingly; keep the type FaceCompareReq and its Text fields but adjust
usages in the Idfy adapter (Interface/Idfy.hs) to map the new names to the
provider's document1/document2 fields (similar to how NameCompareReq uses
extractedName/verifiedName) so external-facing semantics are clear while
preserving provider mapping in the adapter.

356-359: Response type exposes vendor-specific structure.

FaceCompareResp directly wraps Idfy.FaceCompareResponseData, which mirrors the existing NameCompareResp pattern but couples the interface to the Idfy provider. If multi-provider support is anticipated, consider abstracting the response structure to domain types.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs` around
lines 356 - 359, FaceCompareResp currently exposes the vendor-specific
Idfy.FaceCompareResponseData (same anti-pattern as NameCompareResp); replace the
direct wrapper with a domain-level type (e.g., FaceComparisonResult or
FaceCompareRespDomain) that captures only the fields our domain needs, keep or
derive FromJSON/ToJSON/ToSchema for that domain type, and update the Idfy
adapter/translator to map Idfy.FaceCompareResponseData -> FaceComparisonResult
before returning; ensure references to FaceCompareResp in callers are updated to
the new domain type or a small shim constructor to avoid leaking Idfy types into
the interface.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs`:
- Around line 551-553: The current branch unconditionally returns
FaceCompareResp {faceComparedData = Just result} when resp.result is Just
result; instead validate detection flags on the Idfy.FaceCompareResponseData
(inspect fields like face_detected / faceDetection / per-image flags inside the
result) before returning: if either image's face-detected flag is False or
missing, throw an InternalError (use the same error type used elsewhere) rather
than returning match data; update the case in the function handling resp.result
to perform this guard and only set faceComparedData = Just result when detection
succeeded for both images.

---

Nitpick comments:
In `@lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs`:
- Around line 349-353: Rename the vendor-opaque fields in FaceCompareReq to
domain-meaningful names (e.g., change documentImage1/documentImage2 to
selfieImage/documentImage or similar) and update any construction/consumption
sites accordingly; keep the type FaceCompareReq and its Text fields but adjust
usages in the Idfy adapter (Interface/Idfy.hs) to map the new names to the
provider's document1/document2 fields (similar to how NameCompareReq uses
extractedName/verifiedName) so external-facing semantics are clear while
preserving provider mapping in the adapter.
- Around line 356-359: FaceCompareResp currently exposes the vendor-specific
Idfy.FaceCompareResponseData (same anti-pattern as NameCompareResp); replace the
direct wrapper with a domain-level type (e.g., FaceComparisonResult or
FaceCompareRespDomain) that captures only the fields our domain needs, keep or
derive FromJSON/ToJSON/ToSchema for that domain type, and update the Idfy
adapter/translator to map Idfy.FaceCompareResponseData -> FaceComparisonResult
before returning; ensure references to FaceCompareResp in callers are updated to
the new domain type or a small shim constructor to avoid leaking Idfy types into
the interface.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7eaba28a-4405-4974-9f84-bd2368956e77

📥 Commits

Reviewing files that changed from the base of the PR and between 06bb283 and a14d877.

📒 Files selected for processing (7)
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Idfy.hs
  • lib/mobility-core/src/Kernel/External/Verification/Interface/Types.hs
🚧 Files skipped from review as they are similar to previous changes (5)
  • lib/mobility-core/src/Kernel/External/Verification/Interface.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Config.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Request.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Types/Response.hs
  • lib/mobility-core/src/Kernel/External/Verification/Idfy/Client.hs

@RitoBose
RitoBose force-pushed the feat/face-compare-api-addition branch from a14d877 to c6a8b60 Compare June 11, 2026 10:07
@RitikaHanish
RitikaHanish merged commit c72fffc into main Jun 11, 2026
2 checks passed
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.

4 participants