feat/various style improvements and backwards compatible didit migration#883
feat/various style improvements and backwards compatible didit migration#883
Conversation
📝 WalkthroughWalkthroughAdds a BottomSheet UI and migrates many Drawer usages, bumps eid-wallet to 0.6.0, versioned KYC routes to /verification/v2, implements hardware signing fallback in KeyService wired to GlobalState+VaultController, and introduces a LegacyVerificationController to support legacy /verification routes alongside v2. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Client/App
participant KeySvc as KeyService
participant GlobalState as GlobalState
participant VaultCtrl as VaultController
participant SDK as wallet-sdk
App->>KeySvc: signPayload(payload)
activate KeySvc
KeySvc->>KeySvc: attempt hardware signing
KeySvc--xKeySvc: hardware signing fails
KeySvc->>KeySvc: perform software signing fallback
KeySvc->>SDK: persist software-backed context
KeySvc->>KeySvc: runHardwareFallbackCallback(keyId, context, error)
KeySvc->>GlobalState: emit hardwareFallback(keyId, context, error)
deactivate KeySvc
activate GlobalState
GlobalState->>GlobalState: log warning
GlobalState->>VaultCtrl: get vault info
VaultCtrl-->>GlobalState: return vault.ename
GlobalState->>VaultCtrl: syncPublicKey(vault.ename)
VaultCtrl->>SDK: update public key
VaultCtrl-->>GlobalState: sync result
GlobalState->>GlobalState: log result or error
deactivate GlobalState
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
infrastructure/eid-wallet/src/routes/(app)/ePassport/+page.svelte (1)
248-252:⚠️ Potential issue | 🟡 Minor
String.replaceonly substitutes the first space — usereplaceAllor a regex.
rawStatus.toLowerCase().replace(" ", "_")replaces only the first space in the status string. While current known statuses ("approved","declined","in review") each contain at most one space, a backend change returning a multi-word status (e.g."under review") would silently produce"under review"instead of"under_review", falling through to the declined UI without a clear signal.🐛 Proposed fix
- diditResult = rawStatus.toLowerCase().replace(" ", "_") as + diditResult = rawStatus.toLowerCase().replaceAll(" ", "_") as | "approved" | "declined" | "in_review";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(app)/ePassport/+page.svelte around lines 248 - 252, The code uses rawStatus.toLowerCase().replace(" ", "_") which only replaces the first space; update the conversion that sets diditResult (using decision.status and rawStatus) to replace all spaces (e.g., use replaceAll(" ", "_") or rawStatus.replace(/\s+/g, "_")) before the cast so multi-word statuses map correctly to "approved" | "declined" | "in_review"; ensure you normalize casing and whitespace consistently prior to assigning diditResult.infrastructure/eid-wallet/package.json (1)
45-45:⚠️ Potential issue | 🟡 MinorRemove unused
"import": "^0.0.6"production dependency.The
importpackage is a legacy CLI tool from ~11 years ago, not a no-op stub. However, no usages of this package are found in the codebase. If it's not required, remove it frompackage.jsonto keep dependencies clean.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/package.json` at line 45, Remove the unused production dependency "import": "^0.0.6" from package.json; search the repository for any references to the import package to confirm it's unused, then remove the entry for "import" and regenerate the lockfile by running your package manager (npm install or yarn install) so package-lock.json / yarn.lock is updated and CI installs reflect the change.infrastructure/eid-wallet/src/lib/global/controllers/key.ts (1)
157-186:⚠️ Potential issue | 🟠 MajorRemove or gate verbose diagnostic logging — payload hex and full public key are security-sensitive.
The
signPayloadmethod unconditionally logs:
- The raw payload string and its full hex encoding (Lines 162–167), which can contain PII or credential material.
- The complete public key (Line 179), which exposes key material.
These
console.logcalls fire on every sign operation in production.🔒️ Proposed fix — gate behind a debug flag or remove
- console.log(`Payload: "${payload}"`); - console.log(`Payload length: ${payload.length} bytes`); - const payloadHex = Array.from(new TextEncoder().encode(payload)) - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); - console.log(`Payload (hex): ${payloadHex}`); + // Payload details omitted from production logs- console.log(`Public key: ${publicKey.substring(0, 60)}...`); - console.log(`Public key (full): ${publicKey}`); + console.log("Public key available");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/lib/global/controllers/key.ts` around lines 157 - 186, The signPayload method currently emits sensitive logs — remove or gate the raw payload, its hex (payloadHex) and the full public key logs; instead only log non-sensitive context (e.g., keyId, managerType) and a masked public key (first/last few chars) or nothing at all. Replace the unconditional console.log calls around payload, payloadHex and the full public key in signPayload and the getPublicKey try/catch with either: (a) conditional debug-level logging behind a debug flag / environment variable / logger.isDebugEnabled check, or (b) drop them entirely; keep short, non-sensitive messages when needed and avoid printing the full public key or entire payload. Ensure references to payloadHex, console.log lines that print `Payload:`, `Payload (hex):`, and the `Public key (full):` output are removed or gated.infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte (1)
290-294:⚠️ Potential issue | 🟡 Minor
String.replace()only replaces the first occurrence — usereplaceAllor a regex.If the Didit API ever returns a status like
"in review"with multiple spaces (or changes its format),rawStatus.toLowerCase().replace(" ", "_")silently produces a wrong value that won't match any of the"approved" | "declined" | "in_review"union members, causing the UI to fall through to theelse(Verification Failed) branch incorrectly.🐛 Proposed fix
- diditResult = rawStatus.toLowerCase().replace(" ", "_") as + diditResult = rawStatus.toLowerCase().replaceAll(" ", "_") as | "approved" | "declined" | "in_review";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(app)/main/+page.svelte around lines 290 - 294, The code uses rawStatus.toLowerCase().replace(" ", "_") which only replaces the first space and can produce incorrect diditResult; change it to replace all spaces (e.g., rawStatus.toLowerCase().replaceAll(" ", "_") or rawStatus.toLowerCase().replace(/\s+/g, "_")) when computing diditResult from decision.status so the final value reliably matches "approved" | "declined" | "in_review".infrastructure/eid-wallet/src/routes/+layout.svelte (2)
317-322:⚠️ Potential issue | 🟠 Major
checkAuth()is fire-and-forget — unhandled rejections silently swallowed.
checkAuthis anasyncfunction but is invoked withoutawait(and without.catch) in all three deep-link action branches (auth,sign,reveal). Any rejection insidecheckAuth— including failedgotocalls orglobalState.vaultController.vaultaccess errors — is silently dropped. A.catchguard at minimum should be added.🛡️ Proposed fix (apply to all three `checkAuth()` call sites)
- checkAuth(); + checkAuth().catch((error) => { + console.error("Deep link checkAuth failed:", error); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`+layout.svelte around lines 317 - 322, The call to the async function checkAuth() in the deep-link action branches (auth, sign, reveal) is currently fire-and-forget and can swallow rejections (e.g., from goto() or globalState.vaultController.vault); update each call site to handle promise rejections by either awaiting checkAuth() where the caller is async or chaining .catch(...) to log and handle errors (include the error in the log), ensuring any navigation failures from goto() or vault access errors are surfaced and handled.
631-631:⚠️ Potential issue | 🟡 MinorRemove debug
$effectbefore shipping.
$effect(() => console.log("top", safeAreaTop))will fire on everysafeAreaTopchange in production.🔧 Proposed fix
-$effect(() => console.log("top", safeAreaTop));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`+layout.svelte at line 631, Remove the debug $effect that logs safeAreaTop in production: delete the line $effect(() => console.log("top", safeAreaTop)) from the component, or if you want to keep it for local debugging wrap it with a dev-only guard (import { dev } from '$app/environment' and run the $effect only when dev is true) so logging of safeAreaTop does not run in production.infrastructure/eid-wallet/src/routes/(app)/settings/passphrase/+page.svelte (1)
164-177:⚠️ Potential issue | 🟡 MinorPass
dismissible={false}to prevent accidental dismissal without navigation.The BottomSheet defaults to
dismissible: true, allowing users to dismiss the sheet via backdrop click or gesture. Since this doesn't trigger thehandleClosecallback, the navigation to/settingsis skipped. Either disable dismissal entirely withdismissible={false}, or wireonOpenChangeto ensure navigation occurs when the sheet closes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(app)/settings/passphrase/+page.svelte around lines 164 - 177, The BottomSheet currently bound to showSuccessDrawer allows backdrop/gesture dismissal which bypasses handleClose and navigation; update the BottomSheet component (the BottomSheet element that binds isOpen={showSuccessDrawer}) to either add dismissible={false} to prevent accidental dismissal or add an onOpenChange handler that calls handleClose (or triggers the same navigation) when isOpen becomes false so closing always runs the navigation logic tied to handleClose.
🧹 Nitpick comments (13)
infrastructure/eid-wallet/src/routes/(app)/ePassport/+page.svelte (2)
592-597:startSocialBindingPollingcreates a new interval without guarding against an already-running one.If called when
socialBindingPollIntervalis non-null (e.g. due to an unexpected double-call path or future refactoring), a second interval is leaked andstopSocialBindingPollingwill only clear the latest reference, leaving the first running silently.♻️ Proposed fix — add a defensive stop at the top
function startSocialBindingPolling() { + stopSocialBindingPolling(); socialBindingPolling = true; socialBindingPollInterval = setInterval(() => { void runSocialBindingPoll(); }, 3000); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(app)/ePassport/+page.svelte around lines 592 - 597, startSocialBindingPolling currently unconditionally creates a new setInterval which can leak if one already exists; modify startSocialBindingPolling to first guard against an existing interval (check socialBindingPollInterval) and clear it (or call stopSocialBindingPolling()) before creating a new interval, then set socialBindingPolling = true and assign socialBindingPollInterval = setInterval(() => { void runSocialBindingPoll(); }, 3000); ensure the previous interval reference is nulled so stopSocialBindingPolling can reliably clear the active timer.
620-620:{#ifdocData}is always truthy — the guard is misleading.
docDatais initialised to{}on line 29 and never set tonull/undefined, so this condition is alwaystrue. The block renders nothing when the object is empty (becauseObject.entries({})yields no pairs), so there is no runtime bug — but the guard impliesdocDatacould be falsy, which may confuse future readers.♻️ Proposed fix — guard on non-empty entries
- {`#if` docData} + {`#if` Object.keys(docData).length > 0}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(app)/ePassport/+page.svelte at line 620, The template guard {`#if` docData} is misleading because docData is initialized to an empty object and never falsy; change the check to explicitly test for non-empty object entries (e.g., use Object.keys(docData).length > 0 or Object.entries(docData).length) or introduce a boolean like hasDocData derived from docData and use that in the {`#if`} so the intent (render only when docData contains properties) is clear; update the {`#if` docData} block and any related logic that assumes a truthy/falsy docData accordingly.infrastructure/eid-wallet/package.json (2)
28-28:@tailwindcss/container-queriesis redundant with Tailwind CSS v4.Container queries are built into Tailwind v4 natively—this plugin was only needed for v3 and is not registered in any config. Remove from
dependencies.♻️ Proposed removal
- "@tailwindcss/container-queries": "^0.1.1",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/package.json` at line 28, Remove the redundant dependency "@tailwindcss/container-queries" from package.json (it’s unnecessary with Tailwind CSS v4), then reinstall/update lockfiles (npm/yarn/pnpm install) to ensure the dependency is removed from node_modules and the lockfile; also scan for any leftover references to "@tailwindcss/container-queries" in config or code (none expected) and remove them if found.
75-75: Removeautoprefixer— Tailwind CSS v4 makes it redundant.Tailwind v4 uses Lightning CSS for vendor prefixing by default, eliminating the need for the separate autoprefixer plugin. Keeping both can cause redundant prefixing work. Browser target detection comes from your Browserslist configuration.
♻️ Proposed removal
- "autoprefixer": "^10.4.21",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/package.json` at line 75, Remove the redundant "autoprefixer" dependency from package.json (delete the "autoprefixer" entry) and run your package manager install to update lockfiles; also remove or adjust any references to "autoprefixer" in build configs (e.g., postcss.config.js or Vite/webpack config) so Tailwind v4's Lightning CSS handles vendor prefixing and there are no leftover plugin entries.infrastructure/eid-wallet/src/lib/global/controllers/key.ts (2)
63-69:reset()does not clear#onHardwareFallback.If a
KeyServiceinstance isreset()(Lines 63–69) and then reused directly (outside theGlobalState.reset()flow), the stale fallback handler registered before the reset remains active and can reference deallocated or staleVaultController/vaultControllerstate.Since
GlobalState.reset()creates a freshKeyServicethis is safe for the primary usage path, but for any directKeyServicereuse (e.g., tests, isolated usage), this is a latent bug.♻️ Proposed fix
async reset(): Promise<void> { this.#managerCache.clear(); this.#contexts.clear(); await this.#store.delete(CONTEXTS_KEY); await this.#store.delete(READY_KEY); this.#ready = false; + this.#onHardwareFallback = null; }Also applies to: 332-339
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/lib/global/controllers/key.ts` around lines 63 - 69, The reset() method in KeyService currently clears manager/cache and contexts but doesn't clear the stored hardware fallback handler (`#onHardwareFallback`), leaving a stale callback that can reference old VaultController/vaultController state; update KeyService.reset() to also clear/unset `#onHardwareFallback` (and any similar fields referenced elsewhere, e.g., the other reset-like method) so the instance no longer retains the old fallback handler when reused outside GlobalState.reset(), ensuring any registered hardware fallback is removed on reset.
19-23: ExportHardwareFallbackEventto unblock callers of the publicsetHardwareFallbackHandlerAPI.
HardwareFallbackEventis used as the parameter type of the publicsetHardwareFallbackHandlermethod (Line 334) but is not exported. External consumers registering a typed handler cannot reference this type without TypeScript inferring it indirectly.♻️ Proposed fix
-type HardwareFallbackEvent = { +export type HardwareFallbackEvent = { keyId: string; context: KeyServiceContext; originalError: unknown; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/lib/global/controllers/key.ts` around lines 19 - 23, The type HardwareFallbackEvent is declared but not exported while it is used as the parameter type for the public API setHardwareFallbackHandler, so external callers cannot reference it; fix by exporting the type (export type HardwareFallbackEvent = {...}) in the same file where HardwareFallbackEvent is declared (so callers can import it) and ensure any local imports/usages of HardwareFallbackEvent and the public function setHardwareFallbackHandler remain consistent with the exported name.infrastructure/eid-wallet/src/lib/global/state.ts (1)
54-79: Concurrent hardware fallback events can trigger simultaneoussyncPublicKeycalls.If multiple
signPayloadcalls fail on hardware in quick succession (e.g., during batch signing), each awaits the handler independently, resulting in N concurrentsyncPublicKey(vault.ename)invocations. Depending on thesyncPublicKeyimplementation (upsert semantics, server-side rate limits), this could cause duplicate operations or conflicts.Consider debouncing or guarding with an in-progress flag:
♻️ Proposed guard
+ `#syncInProgress` = false; + this.keyService.setHardwareFallbackHandler( async ({ keyId, context, originalError }) => { console.warn(/* ... */); try { + if (this.#syncInProgress) return; + this.#syncInProgress = true; const vault = await this.vaultController.vault; if (!vault?.ename) return; await this.vaultController.syncPublicKey(vault.ename); } catch (syncError) { console.error( "[GlobalState] Failed to sync fallback public key:", syncError, ); + } finally { + this.#syncInProgress = false; } }, );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/lib/global/state.ts` around lines 54 - 79, The hardware fallback handler registered via keyService.setHardwareFallbackHandler can trigger concurrent calls to vaultController.syncPublicKey(vault.ename); add a guard (e.g., an inProgress flag or mutex) inside that async handler to ensure only one syncPublicKey runs at a time for a given vault. Specifically, within the async ({ keyId, context, originalError }) => { ... } closure, check and set a per-vault or global "syncing" marker before awaiting this.vaultController.syncPublicKey(vault.ename), skip or await the existing operation if already in progress, and clear the marker in finally so subsequent fallbacks can proceed; apply this around the existing try/catch where vaultController.vault and vaultController.syncPublicKey are used.infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte (1)
7-7: Remove unusedaxiosimport.
axiosis imported but never called in this file. This adds unnecessary bundle weight.🔧 Proposed fix
-import axios from "axios";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(auth)/review/+page.svelte at line 7, Remove the unused axios import from the Svelte page: delete the line importing axios (import axios from "axios";) from the +page.svelte file since there are no references to axios in this module; ensure no other code relies on axios in this file and run the build to confirm bundle size and lint warnings are resolved.infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte (2)
234-234: Magic 50 ms delay before SDK import is fragile.This
setTimeout(r, 50)is a workaround for a race condition (likely thekycStepstate update not being reflected in the DOM before the SDK mounts into#didit-container-home). Document why this delay is needed, or usetick()from Svelte to flush pending DOM updates instead.♻️ Proposed alternative using `tick`
-import { type Snippet, getContext, onMount } from "svelte"; +import { type Snippet, getContext, onMount, tick } from "svelte"; ... - await new Promise((r) => setTimeout(r, 50)); + await tick(); // flush DOM so `#didit-container-home` is rendered before SDK mounts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(app)/main/+page.svelte at line 234, Replace the fragile await new Promise((r) => setTimeout(r, 50)) hack with Svelte's tick() to flush pending DOM/state updates before mounting the SDK; specifically, import tick from 'svelte' and replace that sleep with await tick() (or await tick() in a small loop if multiple microtasks are needed) in the block that mounts the SDK into "#didit-container-home" and reference the kycStep state update there; also add a short inline comment explaining that tick is used to ensure the DOM reflects the latest kycStep before the SDK queries the container.
104-107:shareQR()is a stub —alert()will break on mobile Tauri.
alert()is a browser API that may not behave as expected (or at all) in Tauri's WebView context, and leaving a stub in production silently breaks the share flow.Would you like me to open a tracking issue or generate a native share implementation using Tauri's
shareplugin?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(app)/main/+page.svelte around lines 104 - 107, shareQR() currently calls alert(), which breaks in Tauri; replace the stub so the function uses a real share flow: remove alert() and implement a platform-aware share—first try the Web Share API (navigator.share) with the QR payload, then if running in Tauri detect the Tauri environment and call the native share plugin (e.g., invoke the share command or use the tauri share plugin API) with the same payload; ensure you handle and log errors and always set shareQRdrawerOpen = false in the finally/cleanup path so the drawer closes even on failure.infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/AuthDrawer.svelte (1)
32-36: Add explicit dialog semantics on the BottomSheet for screen-reader clarity.This keeps modal accessibility behavior consistent with other converted drawers.
♿ Suggested patch
<BottomSheet isOpen={internalOpen} dismissible={false} + role="dialog" + aria-modal="true" + aria-labelledby="auth-drawer-title" class="gap-5" > @@ - <h4 class="text-xl font-bold">Code scanned!</h4> + <h4 id="auth-drawer-title" class="text-xl font-bold">Code scanned!</h4>Also applies to: 57-60
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(app)/scan-qr/components/AuthDrawer.svelte around lines 32 - 36, Add explicit dialog semantics to the BottomSheet instances by adding role="dialog", aria-modal="true", and either an aria-label or aria-labelledby prop (e.g., aria-label="Authentication Drawer" or aria-labelledby referencing the drawer title element) to both BottomSheet usages (the one with internalOpen/class="gap-5" and the other at lines 57-60) so screen readers treat them as modal dialogs and accessibility matches other converted drawers.infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/RevealDrawer.svelte (1)
32-36: Add explicit dialog labeling for accessibility consistency.Other migrated drawers pass dialog semantics explicitly; this one should do the same.
♿ Suggested patch
<BottomSheet isOpen={internalOpen} dismissible={false} + role="dialog" + aria-modal="true" + aria-labelledby="reveal-title" class="gap-5" > @@ - <h4 class="text-xl font-bold text-green-800"> + <h4 id="reveal-title" class="text-xl font-bold text-green-800"> Vote Decrypted </h4> @@ - <h4 class="text-xl font-bold">Reveal Your Blind Vote</h4> + <h4 id="reveal-title" class="text-xl font-bold">Reveal Your Blind Vote</h4>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(app)/scan-qr/components/RevealDrawer.svelte around lines 32 - 36, Add explicit dialog semantics to the BottomSheet in RevealDrawer.svelte: pass role="dialog" and aria-modal="true" and provide either aria-label or aria-labelledby (create an id on the drawer header/title if needed) so the BottomSheet receives the same explicit accessibility props other migrated drawers use; update the BottomSheet JSX/props where isOpen={internalOpen} is set to include these attributes.infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte (1)
145-153: Consider centralizing provisioner v2 request setup.The v2 base path + shared-secret header pattern is repeated in multiple calls. A small helper for URL/header construction would reduce drift risk between onboarding, decision, and upgrade flows.
Also applies to: 211-221, 481-492, 617-627
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/eid-wallet/src/routes/`(auth)/onboarding/+page.svelte around lines 145 - 153, Create a small helper (e.g., getProvisionerRequestConfig or buildProvisionerUrlAndHeaders) that accepts a path like "/verification/v2" and returns the full URL (using PUBLIC_PROVISIONER_URL) plus the headers object containing "x-shared-secret": PUBLIC_PROVISIONER_SHARED_SECRET (or a full axios config). Replace inline uses of new URL(..., PUBLIC_PROVISIONER_URL).toString() and the repeated headers object in the onboarding axios.post call and the other occurrences referenced (decision and upgrade flows) to use this helper so all provisioner v2 requests share a single source of truth.
🤖 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-tauri/tauri.conf.json`:
- Line 31: The Android versionCode in tauri.conf.json was changed from 12 to 22
(property "versionCode") which burns 10 intermediate codes; confirm whether that
jump was intentional and if not set "versionCode" to the correct next
incremental value (e.g., 13) before publishing. Locate the "versionCode" entry
in the tauri.conf.json and update it to the intended strictly-increasing
integer, then verify build/release scripts or CI that may auto-bump it to avoid
reintroducing the jump.
In `@infrastructure/eid-wallet/src/lib/global/controllers/key.ts`:
- Around line 200-251: The fallback currently swallows sync errors causing
unverifiable signatures; update the hardware-fallback flow so failures
propagate: change KeyService.#runHardwareFallbackCallback to not swallow
exceptions (either remove the try/catch or rethrow after logging) so callers of
KeyService.signPayload receive the error, and update the GlobalState callback
(the handler that calls syncPublicKey) to rethrow any syncPublicKey errors
instead of just logging them; additionally guard the fallback path in
KeyService.signPayload so it refuses to fallback when context === "onboarding"
to preserve the existing onboarding invariant.
In `@infrastructure/eid-wallet/src/lib/ui/BottomSheet/BottomSheet.svelte`:
- Around line 23-33: The close handler handleClose currently sets isOpen = false
and also calls onOpenChange?.(false) directly, which causes a duplicate emission
because the reactive $effect watching isOpen will emit again; fix by removing
the direct onOpenChange?.(false) call from handleClose and let the $effect that
compares isOpen and lastReportedOpen handle emitting onOpenChange, or
alternatively update lastReportedOpen inside handleClose (set lastReportedOpen =
false) instead of calling onOpenChange directly so the $effect does not emit a
second time; update only handleClose or lastReportedOpen logic to ensure a
single onOpenChange emission per close.
In `@infrastructure/eid-wallet/src/routes/`(app)/scan-qr/scanLogic.ts:
- Around line 945-946: The comment is outdated and contradicts the call to
globalState.walletSdkAdapter.ensureKey("default", "signing"); — update or remove
the comment to reflect that a separate signing context is explicitly provided:
either change the text to state that the default key is ensured for the
"signing" context, or delete the comment entirely so it does not imply there is
no separate signing context, and keep the ensureKey("default","signing")
invocation as-is.
In `@infrastructure/eid-wallet/src/routes/`(auth)/onboarding/+page.svelte:
- Around line 213-214: The URL template literals interpolate raw IDs into path
segments (e.g., `/verification/v2/decision/${result.session.sessionId}` and the
other template using a session ID at the later occurrence); encode those IDs
with encodeURIComponent before inserting them into the path to avoid breaking
the URL when IDs contain reserved characters (replace
`${result.session.sessionId}` with
`${encodeURIComponent(result.session.sessionId)}` and do the same for the other
session/ID variable used in the second template).
In `@infrastructure/evault-core/src/controllers/LegacyVerificationController.ts`:
- Around line 54-60: The legacy write routes in LegacyVerificationController
(e.g., the POST "/verification/:id/media" handler and the decision endpoints
handling POST/PATCH) must be protected: add a middleware (e.g.,
requireLegacySecretMiddleware) that reads a server-side secret from config/env
(e.g., LEGACY_WRITE_SECRET) and rejects requests missing or with a mismatched
header (e.g., 'x-legacy-secret'); for webhook-style endpoints (e.g., the
"/verification/decisions" handler) add HMAC signature validation middleware
(e.g., validateWebhookSignature) that computes an HMAC of the raw request body
using the shared secret and compares it to a provided signature header (e.g.,
'x-signature') before any state mutation; apply these middlewares to all write
routes in this controller (POST/PATCH handlers) so unauthorized requests are
immediately 401/403 and do not reach handlers like the media upload or
decision-processing functions.
- Around line 85-90: The Veriff outbound calls in LegacyVerificationController
(the veriffClient.post calls) are not wrapped in try/catch, so wrap each
veriffClient.post invocation (the calls around the veriffId/media endpoints
found in LegacyVerificationController) in a try { await veriffClient.post(...) }
catch (err) { log the error and send an appropriate Express error response
(e.g., res.status(502/500).json({ error: 'Veriff request failed', details:
err.message })) to avoid unhandled promise rejections; ensure you handle all
three locations (the blocks around lines noted) and preserve existing success
flows inside the try block.
- Line 224: The assignment to documentId uses null but the
DeepPartial<Verification> expects string | undefined; update the assignment in
LegacyVerificationController (the line setting documentId from docNumber) to
return undefined when docNumber is not a string (e.g., use typeof docNumber ===
"string" ? docNumber : undefined) so the value matches the expected type and
satisfies strict null checks.
---
Outside diff comments:
In `@infrastructure/eid-wallet/package.json`:
- Line 45: Remove the unused production dependency "import": "^0.0.6" from
package.json; search the repository for any references to the import package to
confirm it's unused, then remove the entry for "import" and regenerate the
lockfile by running your package manager (npm install or yarn install) so
package-lock.json / yarn.lock is updated and CI installs reflect the change.
In `@infrastructure/eid-wallet/src/lib/global/controllers/key.ts`:
- Around line 157-186: The signPayload method currently emits sensitive logs —
remove or gate the raw payload, its hex (payloadHex) and the full public key
logs; instead only log non-sensitive context (e.g., keyId, managerType) and a
masked public key (first/last few chars) or nothing at all. Replace the
unconditional console.log calls around payload, payloadHex and the full public
key in signPayload and the getPublicKey try/catch with either: (a) conditional
debug-level logging behind a debug flag / environment variable /
logger.isDebugEnabled check, or (b) drop them entirely; keep short,
non-sensitive messages when needed and avoid printing the full public key or
entire payload. Ensure references to payloadHex, console.log lines that print
`Payload:`, `Payload (hex):`, and the `Public key (full):` output are removed or
gated.
In `@infrastructure/eid-wallet/src/routes/`(app)/ePassport/+page.svelte:
- Around line 248-252: The code uses rawStatus.toLowerCase().replace(" ", "_")
which only replaces the first space; update the conversion that sets diditResult
(using decision.status and rawStatus) to replace all spaces (e.g., use
replaceAll(" ", "_") or rawStatus.replace(/\s+/g, "_")) before the cast so
multi-word statuses map correctly to "approved" | "declined" | "in_review";
ensure you normalize casing and whitespace consistently prior to assigning
diditResult.
In `@infrastructure/eid-wallet/src/routes/`(app)/main/+page.svelte:
- Around line 290-294: The code uses rawStatus.toLowerCase().replace(" ", "_")
which only replaces the first space and can produce incorrect diditResult;
change it to replace all spaces (e.g., rawStatus.toLowerCase().replaceAll(" ",
"_") or rawStatus.toLowerCase().replace(/\s+/g, "_")) when computing diditResult
from decision.status so the final value reliably matches "approved" | "declined"
| "in_review".
In `@infrastructure/eid-wallet/src/routes/`(app)/settings/passphrase/+page.svelte:
- Around line 164-177: The BottomSheet currently bound to showSuccessDrawer
allows backdrop/gesture dismissal which bypasses handleClose and navigation;
update the BottomSheet component (the BottomSheet element that binds
isOpen={showSuccessDrawer}) to either add dismissible={false} to prevent
accidental dismissal or add an onOpenChange handler that calls handleClose (or
triggers the same navigation) when isOpen becomes false so closing always runs
the navigation logic tied to handleClose.
In `@infrastructure/eid-wallet/src/routes/`+layout.svelte:
- Around line 317-322: The call to the async function checkAuth() in the
deep-link action branches (auth, sign, reveal) is currently fire-and-forget and
can swallow rejections (e.g., from goto() or globalState.vaultController.vault);
update each call site to handle promise rejections by either awaiting
checkAuth() where the caller is async or chaining .catch(...) to log and handle
errors (include the error in the log), ensuring any navigation failures from
goto() or vault access errors are surfaced and handled.
- Line 631: Remove the debug $effect that logs safeAreaTop in production: delete
the line $effect(() => console.log("top", safeAreaTop)) from the component, or
if you want to keep it for local debugging wrap it with a dev-only guard (import
{ dev } from '$app/environment' and run the $effect only when dev is true) so
logging of safeAreaTop does not run in production.
---
Nitpick comments:
In `@infrastructure/eid-wallet/package.json`:
- Line 28: Remove the redundant dependency "@tailwindcss/container-queries" from
package.json (it’s unnecessary with Tailwind CSS v4), then reinstall/update
lockfiles (npm/yarn/pnpm install) to ensure the dependency is removed from
node_modules and the lockfile; also scan for any leftover references to
"@tailwindcss/container-queries" in config or code (none expected) and remove
them if found.
- Line 75: Remove the redundant "autoprefixer" dependency from package.json
(delete the "autoprefixer" entry) and run your package manager install to update
lockfiles; also remove or adjust any references to "autoprefixer" in build
configs (e.g., postcss.config.js or Vite/webpack config) so Tailwind v4's
Lightning CSS handles vendor prefixing and there are no leftover plugin entries.
In `@infrastructure/eid-wallet/src/lib/global/controllers/key.ts`:
- Around line 63-69: The reset() method in KeyService currently clears
manager/cache and contexts but doesn't clear the stored hardware fallback
handler (`#onHardwareFallback`), leaving a stale callback that can reference old
VaultController/vaultController state; update KeyService.reset() to also
clear/unset `#onHardwareFallback` (and any similar fields referenced elsewhere,
e.g., the other reset-like method) so the instance no longer retains the old
fallback handler when reused outside GlobalState.reset(), ensuring any
registered hardware fallback is removed on reset.
- Around line 19-23: The type HardwareFallbackEvent is declared but not exported
while it is used as the parameter type for the public API
setHardwareFallbackHandler, so external callers cannot reference it; fix by
exporting the type (export type HardwareFallbackEvent = {...}) in the same file
where HardwareFallbackEvent is declared (so callers can import it) and ensure
any local imports/usages of HardwareFallbackEvent and the public function
setHardwareFallbackHandler remain consistent with the exported name.
In `@infrastructure/eid-wallet/src/lib/global/state.ts`:
- Around line 54-79: The hardware fallback handler registered via
keyService.setHardwareFallbackHandler can trigger concurrent calls to
vaultController.syncPublicKey(vault.ename); add a guard (e.g., an inProgress
flag or mutex) inside that async handler to ensure only one syncPublicKey runs
at a time for a given vault. Specifically, within the async ({ keyId, context,
originalError }) => { ... } closure, check and set a per-vault or global
"syncing" marker before awaiting
this.vaultController.syncPublicKey(vault.ename), skip or await the existing
operation if already in progress, and clear the marker in finally so subsequent
fallbacks can proceed; apply this around the existing try/catch where
vaultController.vault and vaultController.syncPublicKey are used.
In `@infrastructure/eid-wallet/src/routes/`(app)/ePassport/+page.svelte:
- Around line 592-597: startSocialBindingPolling currently unconditionally
creates a new setInterval which can leak if one already exists; modify
startSocialBindingPolling to first guard against an existing interval (check
socialBindingPollInterval) and clear it (or call stopSocialBindingPolling())
before creating a new interval, then set socialBindingPolling = true and assign
socialBindingPollInterval = setInterval(() => { void runSocialBindingPoll(); },
3000); ensure the previous interval reference is nulled so
stopSocialBindingPolling can reliably clear the active timer.
- Line 620: The template guard {`#if` docData} is misleading because docData is
initialized to an empty object and never falsy; change the check to explicitly
test for non-empty object entries (e.g., use Object.keys(docData).length > 0 or
Object.entries(docData).length) or introduce a boolean like hasDocData derived
from docData and use that in the {`#if`} so the intent (render only when docData
contains properties) is clear; update the {`#if` docData} block and any related
logic that assumes a truthy/falsy docData accordingly.
In `@infrastructure/eid-wallet/src/routes/`(app)/main/+page.svelte:
- Line 234: Replace the fragile await new Promise((r) => setTimeout(r, 50)) hack
with Svelte's tick() to flush pending DOM/state updates before mounting the SDK;
specifically, import tick from 'svelte' and replace that sleep with await tick()
(or await tick() in a small loop if multiple microtasks are needed) in the block
that mounts the SDK into "#didit-container-home" and reference the kycStep state
update there; also add a short inline comment explaining that tick is used to
ensure the DOM reflects the latest kycStep before the SDK queries the container.
- Around line 104-107: shareQR() currently calls alert(), which breaks in Tauri;
replace the stub so the function uses a real share flow: remove alert() and
implement a platform-aware share—first try the Web Share API (navigator.share)
with the QR payload, then if running in Tauri detect the Tauri environment and
call the native share plugin (e.g., invoke the share command or use the tauri
share plugin API) with the same payload; ensure you handle and log errors and
always set shareQRdrawerOpen = false in the finally/cleanup path so the drawer
closes even on failure.
In
`@infrastructure/eid-wallet/src/routes/`(app)/scan-qr/components/AuthDrawer.svelte:
- Around line 32-36: Add explicit dialog semantics to the BottomSheet instances
by adding role="dialog", aria-modal="true", and either an aria-label or
aria-labelledby prop (e.g., aria-label="Authentication Drawer" or
aria-labelledby referencing the drawer title element) to both BottomSheet usages
(the one with internalOpen/class="gap-5" and the other at lines 57-60) so screen
readers treat them as modal dialogs and accessibility matches other converted
drawers.
In
`@infrastructure/eid-wallet/src/routes/`(app)/scan-qr/components/RevealDrawer.svelte:
- Around line 32-36: Add explicit dialog semantics to the BottomSheet in
RevealDrawer.svelte: pass role="dialog" and aria-modal="true" and provide either
aria-label or aria-labelledby (create an id on the drawer header/title if
needed) so the BottomSheet receives the same explicit accessibility props other
migrated drawers use; update the BottomSheet JSX/props where
isOpen={internalOpen} is set to include these attributes.
In `@infrastructure/eid-wallet/src/routes/`(auth)/onboarding/+page.svelte:
- Around line 145-153: Create a small helper (e.g., getProvisionerRequestConfig
or buildProvisionerUrlAndHeaders) that accepts a path like "/verification/v2"
and returns the full URL (using PUBLIC_PROVISIONER_URL) plus the headers object
containing "x-shared-secret": PUBLIC_PROVISIONER_SHARED_SECRET (or a full axios
config). Replace inline uses of new URL(..., PUBLIC_PROVISIONER_URL).toString()
and the repeated headers object in the onboarding axios.post call and the other
occurrences referenced (decision and upgrade flows) to use this helper so all
provisioner v2 requests share a single source of truth.
In `@infrastructure/eid-wallet/src/routes/`(auth)/review/+page.svelte:
- Line 7: Remove the unused axios import from the Svelte page: delete the line
importing axios (import axios from "axios";) from the +page.svelte file since
there are no references to axios in this module; ensure no other code relies on
axios in this file and run the build to confirm bundle size and lint warnings
are resolved.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (30)
infrastructure/eid-wallet/package.jsoninfrastructure/eid-wallet/src-tauri/tauri.conf.jsoninfrastructure/eid-wallet/src/lib/global/controllers/key.tsinfrastructure/eid-wallet/src/lib/global/state.tsinfrastructure/eid-wallet/src/lib/ui/BottomSheet/BottomSheet.svelteinfrastructure/eid-wallet/src/lib/ui/CameraPermissionDialog/CameraPermissionDialog.svelteinfrastructure/eid-wallet/src/lib/ui/index.tsinfrastructure/eid-wallet/src/routes/(app)/+layout.svelteinfrastructure/eid-wallet/src/routes/(app)/ePassport/+page.svelteinfrastructure/eid-wallet/src/routes/(app)/main/+page.svelteinfrastructure/eid-wallet/src/routes/(app)/scan-qr/components/AuthDrawer.svelteinfrastructure/eid-wallet/src/routes/(app)/scan-qr/components/LoggedInDrawer.svelteinfrastructure/eid-wallet/src/routes/(app)/scan-qr/components/RevealDrawer.svelteinfrastructure/eid-wallet/src/routes/(app)/scan-qr/components/SigningDrawer.svelteinfrastructure/eid-wallet/src/routes/(app)/scan-qr/components/SocialBindingDrawer.svelteinfrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.tsinfrastructure/eid-wallet/src/routes/(app)/settings/+page.svelteinfrastructure/eid-wallet/src/routes/(app)/settings/passphrase/+page.svelteinfrastructure/eid-wallet/src/routes/(app)/settings/pin/+page.svelteinfrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelteinfrastructure/eid-wallet/src/routes/(auth)/login/+page.svelteinfrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelteinfrastructure/eid-wallet/src/routes/(auth)/register/+page.svelteinfrastructure/eid-wallet/src/routes/(auth)/review/+page.svelteinfrastructure/eid-wallet/src/routes/+layout.svelteinfrastructure/evault-core/src/controllers/LegacyVerificationController.tsinfrastructure/evault-core/src/controllers/SessionIdValidation.spec.tsinfrastructure/evault-core/src/controllers/VerificationController.tsinfrastructure/evault-core/src/controllers/VerificationRoutesCompatibility.spec.tsinfrastructure/evault-core/src/index.ts
infrastructure/evault-core/src/controllers/LegacyVerificationController.ts
Show resolved
Hide resolved
infrastructure/evault-core/src/controllers/LegacyVerificationController.ts
Show resolved
Hide resolved
infrastructure/evault-core/src/controllers/LegacyVerificationController.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
infrastructure/evault-core/src/controllers/LegacyVerificationController.ts (2)
54-60:⚠️ Potential issue | 🔴 CriticalProtect all legacy write routes with server-side auth/signature checks.
POST/PATCHlegacy endpoints are still mutable without route-level authentication, including the decisions webhook. This leaves verification state writable by unauthorized callers.🔐 Suggested route hardening
+import { + requireLegacySecretMiddleware, + validateLegacyWebhookSignatureMiddleware, +} from "../middleware/legacyAuth"; - app.post("/verification/:id/media", async (req: Request, res: Response) => { + app.post("/verification/:id/media", requireLegacySecretMiddleware, async (req: Request, res: Response) => { - app.post("/verification", async (req: Request, res: Response) => { + app.post("/verification", requireLegacySecretMiddleware, async (req: Request, res: Response) => { - app.patch("/verification/:id", async (req: Request, res: Response) => { + app.patch("/verification/:id", requireLegacySecretMiddleware, async (req: Request, res: Response) => { - app.post("/verification/decisions", async (req: Request, res: Response) => { + app.post("/verification/decisions", validateLegacyWebhookSignatureMiddleware, async (req: Request, res: Response) => {Also applies to: 104-112, 147-156, 179-186
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/evault-core/src/controllers/LegacyVerificationController.ts` around lines 54 - 60, Legacy write routes in LegacyVerificationController (e.g., the POST /verification/:id/media handler and other POST/PATCH handlers) are unprotected and must enforce server-side authentication/signature checks; update each mutable route handler (handlers registered via app.post and app.patch in LegacyVerificationController) to run the existing server auth middleware or a new verifyServerSignature function before processing (reject with 401/403 on failure), and ensure the decisions webhook and all mentioned endpoints (the handlers around the other referenced blocks) use the same middleware so only signed/authorized requests can mutate verification state.
85-90:⚠️ Potential issue | 🔴 CriticalHandle Veriff API failures explicitly in async route handlers.
These outbound calls are not wrapped locally. On Express 4 setups without async-error wrappers, rejected promises can bypass consistent HTTP error responses and reduce reliability.
🛟 Suggested error-handling pattern
- await veriffClient.post(`/v1/sessions/${veriffId}/media`, veriffBody, { ... }); + try { + await veriffClient.post(`/v1/sessions/${veriffId}/media`, veriffBody, { ... }); + } catch (error) { + return res.status(502).json({ error: "Veriff request failed" }); + }Apply the same pattern to the calls on Line 126 and Line 169.
Use this read-only check to confirm whether the project already installs a global async wrapper (which would change severity/handling expectations):
#!/bin/bash set -euo pipefail echo "== Express dependency declarations ==" fd package.json -x rg -n '"express"\s*:' {} echo echo "== Common async error wrappers (if any) ==" rg -n --type ts --type js 'express-async-errors|express-async-handler|wrapAsync|catchAsync|asyncHandler' echo echo "== Async route handlers in LegacyVerificationController ==" rg -n --type ts 'app\.(get|post|patch)\([^,]+,\s*async\s*\(' infrastructure/evault-core/src/controllers/LegacyVerificationController.tsAlso applies to: 126-135, 169-174
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/evault-core/src/controllers/LegacyVerificationController.ts` around lines 85 - 90, The Veriff API calls (the veriffClient.post(...) invocations using veriffId, veriffBody and signature) must be wrapped in try/catch blocks inside LegacyVerificationController async route handlers so rejected promises don't escape; for each occurrence (the call shown plus the ones around lines 126 and 169) catch errors and call next(err) (or map to res.status(502).json({ message: 'Upstream Veriff error', error: err.message })) to ensure Express error middleware handles failures consistently. Ensure you reference the same variables (veriffId, veriffBody, signature) and don't swallow the original error when rethrowing or passing to next().
🤖 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 80-83: The code currently casts process.env.VERIFF_HMAC_KEY with
"as string" when calling createHmacSignature (used in
LegacyVerificationController), which hides missing-config errors; instead
validate and fail-fast: read VERIFF_HMAC_KEY once (e.g., in module init or the
LegacyVerificationController constructor) and throw a clear error if undefined,
then pass that validated string to createHmacSignature and any other places
referenced (the other calls around the same controller at the spots matching
121-124 and 164-167). Optionally introduce a small helper like
getEnvOrThrow('VERIFF_HMAC_KEY') to centralize the check and replace all "as
string" casts with the validated value.
- Around line 114-145: The code currently calls verificationService.create(...)
before calling veriffClient.post(...), which can leave a DB record without
veriffId if the external call fails; change the flow so the external Veriff
session is created first (call createHmacSignature(...) and
veriffClient.post(...) to obtain veriffSession) and only then call
verificationService.create(...) with vendorData/veriffId set, or alternatively
perform verificationService.create(...) inside a transaction and roll
back/delete the created record if veriffClient.post(...) fails; update usages of
verification, verification.id, and findByIdAndUpdate(...) accordingly so no
partially-initialized verification remains on Veriff errors.
---
Duplicate comments:
In `@infrastructure/evault-core/src/controllers/LegacyVerificationController.ts`:
- Around line 54-60: Legacy write routes in LegacyVerificationController (e.g.,
the POST /verification/:id/media handler and other POST/PATCH handlers) are
unprotected and must enforce server-side authentication/signature checks; update
each mutable route handler (handlers registered via app.post and app.patch in
LegacyVerificationController) to run the existing server auth middleware or a
new verifyServerSignature function before processing (reject with 401/403 on
failure), and ensure the decisions webhook and all mentioned endpoints (the
handlers around the other referenced blocks) use the same middleware so only
signed/authorized requests can mutate verification state.
- Around line 85-90: The Veriff API calls (the veriffClient.post(...)
invocations using veriffId, veriffBody and signature) must be wrapped in
try/catch blocks inside LegacyVerificationController async route handlers so
rejected promises don't escape; for each occurrence (the call shown plus the
ones around lines 126 and 169) catch errors and call next(err) (or map to
res.status(502).json({ message: 'Upstream Veriff error', error: err.message }))
to ensure Express error middleware handles failures consistently. Ensure you
reference the same variables (veriffId, veriffBody, signature) and don't swallow
the original error when rethrowing or passing to next().
Description of change
Issue Number
Type of change
How the change has been tested
Change checklist
Summary by CodeRabbit
New Features
Updates