Skip to content

fix: recovery fallback#885

Merged
coodos merged 8 commits intomainfrom
feat/recovery-fixes
Feb 25, 2026
Merged

fix: recovery fallback#885
coodos merged 8 commits intomainfrom
feat/recovery-fixes

Conversation

@coodos
Copy link
Contributor

@coodos coodos commented Feb 25, 2026

Description of change

Issue Number

Type of change

  • Update (a change which updates existing functionality)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

  • New Features

    • Duplicate identity detection: users are notified if an existing eVault matches their identity and offered recover-or-start-over options.
    • Full-screen dialogs for several QR/auth flows for improved layout on mobile.
  • Improvements

    • Document number normalization for more reliable verification and duplicate detection.
    • Automatic passphrase-recovery fallback when the backend indicates a missing policy.
    • Smoother recovery completion by ensuring keys and syncing public identity before finalizing.
  • Tests

    • Added tests covering recovery-by-document and lookup-by-document validation.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Warning

Rate limit exceeded

@coodos has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 8869921 and 0819642.

📒 Files selected for processing (1)
  • infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte
📝 Walkthrough

Walkthrough

Adds duplicate-identity detection and recovery: document-number normalization across services, a verification lookup-by-document endpoint, recovery fallback that backfills binding documents and provisioning signatures, onboarding UI handling for duplicate results, and tests covering lookup and recovery flows.

Changes

Cohort / File(s) Summary
Frontend Onboarding
infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte
Adds duplicate branch to diditResult, new state duplicateExistingW3id/duplicateDocumentNumber, resets duplicate state at transitions, performs duplicate lookup on provisioning, and renders a duplicate-result UI with recover/restart options.
Verification Controller & Lookup
infrastructure/evault-core/src/controllers/VerificationController.ts, infrastructure/evault-core/src/controllers/LegacyVerificationController.ts, infrastructure/evault-core/src/controllers/VerificationLookupByDocument.spec.ts, infrastructure/evault-core/src/controllers/SessionIdValidation.spec.ts
Adds /verification/v2/lookup-by-document/:sessionId endpoint, validates sessionId UUID and shared secret, normalizes document numbers, queries verification records (with backward-compat fallback), and adds tests including sessionId validation and lookup behavior.
Provisioning Service
infrastructure/evault-core/src/services/ProvisioningService.ts
Introduces normalizeDocumentNumber helper and uses normalized document numbers in duplicate checks and provisioning/upgrade flows.
Recovery Controller & Tests
infrastructure/evault-core/src/controllers/RecoveryController.ts, infrastructure/evault-core/src/controllers/RecoveryDocumentFallback.spec.ts
Large addition: integrates signAsProvisioner, adds helpers (normalizeDocumentNumber, toIdVerifPayload, platform token fetch, binding-document fetch/create, ensureRecoveryBindingDocuments), extends face-search recovery with document-number fallback and binding-document backfill; includes unit test for document-number-based fallback.
Evault UI Recover Flow
infrastructure/eid-wallet/src/routes/(public)/recover/+page.svelte
Adds automatic recoverVault() fallback when passphrase policy is missing (404 or policy-not-found messages) in passphrase status failure path.
e-passport onboarding key sync
infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte
Ensures default onboarding key exists and synchronizes recovered eName public key before finishing recovery flow.
BottomSheet UI / Drawers
infrastructure/eid-wallet/src/lib/ui/BottomSheet/BottomSheet.svelte, .../scan-qr/components/*Drawer.svelte
Adds fullScreen?: boolean prop to BottomSheet and enables fullScreen mode in several drawer components; adjusts layout, typographic sizes, and container flex behavior for full-screen presentation.
Build scripts
infrastructure/eid-wallet/package.json
Adds build:apk and build:aab npm scripts for Tauri Android builds.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/UI
    participant Onboarding as Onboarding Page
    participant ProvisionSvc as Provisioning Service
    participant DiditAPI as DIDIT API
    participant VerifSvc as Verification Service

    Client->>Onboarding: Start provisioning (Didit session)
    Onboarding->>ProvisionSvc: Begin provisioning flow with sessionId
    ProvisionSvc->>DiditAPI: Fetch decision for sessionId
    DiditAPI-->>ProvisionSvc: Decision (approved/declined/in_review/duplicate)
    alt Decision is duplicate
        ProvisionSvc->>VerifSvc: GET /verification/v2/lookup-by-document/:sessionId
        VerifSvc-->>ProvisionSvc: { success:true, existingW3id, documentNumber } or { success:false }
        ProvisionSvc-->>Onboarding: set diditResult = "duplicate" + duplicate data
        Onboarding->>Client: Render duplicate UI (recover or restart)
    else Not duplicate
        ProvisionSvc-->>Onboarding: Continue normal provisioning
        Onboarding->>Client: Complete onboarding
    end
Loading
sequenceDiagram
    participant Client as Recovery Client
    participant RecoveryCtrl as Recovery Controller
    participant FaceSearch as Face-Search Service
    participant VerifSvc as Verification Service
    participant Platform as Platform Token Registry
    participant eVault as eVault Service

    Client->>RecoveryCtrl: POST /recovery/face-search (payload)
    RecoveryCtrl->>FaceSearch: Attempt face-search
    alt Face match found
        FaceSearch-->>RecoveryCtrl: idVerif + w3id
        RecoveryCtrl-->>Client: Respond success with idVerif
    else No face match
        RecoveryCtrl->>VerifSvc: Query by normalized documentNumber
        VerifSvc-->>RecoveryCtrl: Candidate record with linkedEName (or none)
        alt Candidate found
            RecoveryCtrl->>Platform: request platform token
            Platform-->>RecoveryCtrl: token
            RecoveryCtrl->>eVault: fetch binding document types (w3id, token)
            eVault-->>RecoveryCtrl: existing types
            RecoveryCtrl->>eVault: create missing binding docs (id_document, photograph)
            eVault-->>RecoveryCtrl: created
            RecoveryCtrl-->>Client: Respond success with w3id and idVerif
        else No candidate
            RecoveryCtrl-->>Client: Respond failure / no match
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

evault-refactor

Suggested reviewers

  • sosweetham

Poem

🐇 I sniffed a twin in numbered file,
I trimmed and uppercased every tile,
I hopped through bindings, signed and spun,
Recovered names till the job was done,
Tiny paws, big fixes — springtime smile 🌱

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete; it uses the template structure but provides no actual implementation details, test description, or explanation of the 'recovery fallback' changes despite substantial code modifications across 9+ files. Add detailed description of what the recovery fallback feature does, which files were changed and why, and describe how the changes were tested.
Title check ❓ Inconclusive The title 'fix: recovery fallback' is vague and generic, using non-descriptive terms that don't clearly convey what aspect of recovery fallback is being fixed. Provide a more specific title that describes the actual fix, such as 'fix: add document number fallback recovery for unmatched face searches' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/recovery-fixes

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.

Copy link
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: 3

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

Inline comments:
In `@infrastructure/evault-core/src/controllers/LegacyVerificationController.ts`:
- Around line 202-210: The duplicate check in LegacyVerificationController
currently only queries the normalized docNumber (trimmed/uppercased), which
misses legacy rows stored with original casing/spacing; update the
verificationService.findManyAndCount call to search for both the normalized form
and legacy/raw variants (e.g., the trimmed original string and a
case-insensitive match) so historical documentId values are matched; locate the
code around document.number.value, docNumber and the findManyAndCount call and
change the query payload to include an OR/IN condition (or a case-insensitive
comparison) on documentId to cover both normalized and legacy IDs while keeping
the existing DUPLICATES_POLICY check.

In `@infrastructure/evault-core/src/controllers/RecoveryController.ts`:
- Around line 101-122: The GraphQL mutation POSTs in RecoveryController.ts
currently treat any 200 response as success and ignore GraphQL errors; update
the code after the Axios.post call that invokes the CreateBindingDocument
mutation to inspect response.data for data.createBindingDocument.errors (and
response.data.errors if present) and if any errors exist, log them with context
(including subject and mutation name) and throw or return a failed result so the
backfill/flow doesn't silently succeed; apply the same explicit error
check/handling to both places where CreateBindingDocument is called (the two
Axios.post blocks that send input { subject, type: "id_document", data,
ownerSignature } to gqlUrl with Authorization `Bearer ${token}`) so
GraphQL-level errors are surfaced and handled.

In `@infrastructure/evault-core/src/services/ProvisioningService.ts`:
- Around line 56-61: The duplicate-check only queries
verificationService.findManyAndCount by the normalized documentId
(normalizeDocumentNumber) and misses historical non-normalized rows; update the
logic in ProvisioningService where documentNumber is handled so that after
computing normalizedDocumentNumber you either (a) query
verificationService.findManyAndCount with an OR that checks both { documentId:
normalizedDocumentNumber } and { documentId: documentNumber } or (b) if the
normalized query returns zero, run a fallback findManyAndCount using the raw
documentNumber; ensure you reference the same variables (documentNumber,
normalizedDocumentNumber) and use the existing
verificationService.findManyAndCount call so docMatches covers both normalized
and legacy raw values.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fbce5c6 and 582ac8d.

📒 Files selected for processing (8)
  • infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte
  • infrastructure/evault-core/src/controllers/LegacyVerificationController.ts
  • infrastructure/evault-core/src/controllers/RecoveryController.ts
  • infrastructure/evault-core/src/controllers/RecoveryDocumentFallback.spec.ts
  • infrastructure/evault-core/src/controllers/SessionIdValidation.spec.ts
  • infrastructure/evault-core/src/controllers/VerificationController.ts
  • infrastructure/evault-core/src/controllers/VerificationLookupByDocument.spec.ts
  • infrastructure/evault-core/src/services/ProvisioningService.ts

Copy link
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: 4

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

Inline comments:
In `@infrastructure/eid-wallet/src/lib/ui/BottomSheet/BottomSheet.svelte`:
- Around line 50-57: The fullscreen sheet currently uses "z-50" which places it
above the backdrop, making dismissible taps unreachable; update the class logic
in BottomSheet.svelte so that when fullScreen && dismissible the sheet does not
use a higher z-index than the backdrop (e.g., remove or lower "z-50" on the
fullscreen branch and ensure the backdrop element has the higher z-index),
keeping interactive children working (you can scope a pointer-events-auto to
inner content if needed); adjust the conditional that builds the fullScreen
class string and the backdrop's z-index so taps hit the backdrop when
dismissible is true.

In
`@infrastructure/eid-wallet/src/routes/`(app)/scan-qr/components/AuthDrawer.svelte:
- Around line 32-37: The BottomSheet dialog rendered in AuthDrawer (the
BottomSheet component instance using isOpen={internalOpen}) lacks an accessible
name; add an aria-labelledby prop on the BottomSheet pointing to the id of the
heading inside the sheet (create a unique id like auth-drawer-title and assign
it to the heading element used as the dialog title), and ensure both BottomSheet
instances (the one at the top and the other at lines ~58-60) use the
corresponding aria-labelledby IDs so the dialog is properly labeled for
assistive technologies.

In
`@infrastructure/eid-wallet/src/routes/`(app)/scan-qr/components/RevealDrawer.svelte:
- Around line 32-37: The BottomSheet dialog in RevealDrawer.svelte is missing
accessible labeling; update the BottomSheet invocation(s) (the component used
with isOpen={internalOpen}) to include an aria-labelledby or aria-label prop and
bind it to the rendered title element: give the title element (e.g., the hX that
displays the drawer title) a stable id (like revealDrawerTitle) and set
aria-labelledby to that id on the BottomSheet; apply the same pattern to the
other BottomSheet usages in this file where a title is rendered so screen
readers get proper context.

In `@infrastructure/eid-wallet/src/routes/`(auth)/e-passport/+page.svelte:
- Around line 18-27: The recovery finalization currently commits side effects
(setting RECOVERY_SKIP_PROFILE_SETUP_KEY in localStorage, updating
globalState.vaultController.vault, and clearing pendingRecovery) before awaited
operations complete, risking partial state on failure; change the flow so you
first await globalState.walletSdkAdapter.ensureKey("default","onboarding") and
await globalState.vaultController.syncPublicKey(recovery.ename), then only after
both awaits succeed assign globalState.vaultController.vault = {uri:
recovery.uri, ename: recovery.ename}, set
localStorage.setItem(RECOVERY_SKIP_PROFILE_SETUP_KEY, "true"), and call
pendingRecovery.set(null) so all side effects are committed atomically;
reference these symbols: RECOVERY_SKIP_PROFILE_SETUP_KEY,
globalState.walletSdkAdapter.ensureKey, globalState.vaultController.vault,
globalState.vaultController.syncPublicKey, pendingRecovery.set.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9ce760 and 8869921.

📒 Files selected for processing (8)
  • infrastructure/eid-wallet/package.json
  • infrastructure/eid-wallet/src/lib/ui/BottomSheet/BottomSheet.svelte
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/AuthDrawer.svelte
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/LoggedInDrawer.svelte
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/RevealDrawer.svelte
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/SigningDrawer.svelte
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/SocialBindingDrawer.svelte
  • infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte

@coodos coodos merged commit e778d73 into main Feb 25, 2026
6 checks passed
@coodos coodos deleted the feat/recovery-fixes branch February 25, 2026 13:36
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