Skip to content

fix(auth): recover verified sessions and guard wallet initialization - #3032

Merged
innolope-dev merged 3 commits into
devfrom
innolope/sprint159-auth
Sep 8, 2026
Merged

innolope-dev merged 3 commits into
devfrom
innolope/sprint159-auth

Conversation

@innolope-dev

@innolope-dev innolope-dev commented Sep 7, 2026 •

Copy link
Copy Markdown
Collaborator

After a successful passkey verification, login can stall before loading the authenticated user, and a second ceremony can race that transition. Login now cancels obsolete user queries, retries session hydration with a deadline, and keeps the transition exclusive until the verified key can be published. User responses that belong to a replaced or cleared session cannot update the session token or user store.

Kernel initialization now respects build ownership: obsolete results cannot force logout or overwrite a rebuild, and replacing a credential invalidates its cached signing clients while the new client initializes. Session-key grants await client readiness. The auto-balance entry point stays usable while the wallet is not ready so a failed initialization can surface its existing retry/skip recovery instead of trapping the user behind a disabled Continue button. Genuine stale-key rejection remains enforced.

Passkey verification retains the session token on web as well as native before invite acceptance and badge claims. Retry/HTTP diagnostics become breadcrumbs, and the signup component owns the single unexpected-failure exception and normalized analytics event. Expected cancellation/readability errors do not create exception duplicates. Register 409 USERNAME_TAKEN is preserved before the SDK decodes an error response as credential data. A collision after the availability check now shows the existing username-taken login recovery, emits one normalized failure event, and does not retry registration or rename the username.

Setup now shows retry and support actions when the current or entry step is missing, initialization fails, or layout/session/authenticator initialization exceeds its deadline. Retry reloads the page to restart the layout and session providers. Late or unmounted initialization results cannot change the selected step. Existing localized wording and the support drawer are reused.

Tasks: TASK-21392, TASK-21391, TASK-21644, TASK-21932, TASK-21404, and TASK-21385.

Validation:

  • Reproduced the missing web token capture and kernel disposal/rebuild/credential-switch races before fixing them.
  • All 158 targeted tests across 18 suites pass (including the final setup-suite rerun). They cover session retry/deadline/cancellation, replacement-session responses, ceremony exclusivity, kernel ownership/readiness, session-key grants, fresh-cookie-jar invite/badge requests through their real service functions, and telemetry deduplication, username-collision recovery, and missing/timed-out/failed setup initialization on web and native branches.
  • Follow-up review validation: reproduced the disabled-Continue lockout, then passed 27 targeted modal, grant and kernel tests, full typecheck, targeted ESLint and Prettier. A readiness failure does not submit an approval and releases the grant loading state.
  • Existing fix: signup finish screen, iOS install dead end, and six defects found testing dev #2880 duplicate-navigation regression remains covered; that fix is not reimplemented.
  • Current-head review is clean and required CI passes (6,408 unit tests, native export and preview). The advisory screenshot rerun also passed on September 8: 68 fixture captures at 320/430px. The report compares cached fd63d01 against GitHub merge commit 59a44c4 (this head plus newer dev 7bd1669), so its 19 changed captures include subsequent dev work. Inspected the largest avatar/payment-channel differences and traced them to that dev update; those components and English messages are unchanged in this PR. This is fixture evidence, not real-device ceremony acceptance.
  • Typecheck, targeted ESLint, Prettier, and whitespace checks pass. ESLint retains existing hook/navigation warnings in Auth/useZeroDev and the setup entry effect; no lint errors.

Remaining acceptance gaps:

  • iOS/Android ceremony and wallet-signing acceptance, an invited web signup against the deployed API, and post-rollout Sentry/PostHog recurrence checks remain outstanding. No task is marked Done on unit tests alone.

Based on origin/dev at fd63d01; existing #2801, #2880 and #3002 were inspected before implementation. No new app copy is introduced.

@vercel

vercel Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
peanut-wallet Ready Ready Preview Sep 8, 2026 12:47am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 3ef8d3c1-94bb-449d-8a10-ed147fd716b2

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7283.46 → 7287.43 (+3.97)
Findings: +1 net (+63 new, -62 resolved)

🆕 New findings (63)

  • critical complexity — src/context/kernelClient.context.tsx — CC 115, MI 56.58, SLOC 550
  • critical complexity — src/app/(setup)/setup/page.tsx — CC 85, MI 56.22, SLOC 308
  • critical complexity — src/utils/webauthn.utils.ts — CC 58, MI 53.63, SLOC 201
  • high hotspot — src/context/authContext.tsx — 54 commits, +352/-208 lines since 6 months ago
  • high hotspot — src/hooks/useZeroDev.ts — 52 commits, +649/-392 lines since 6 months ago
  • high complexity — src/components/Setup/Views/SetupPasskey.tsx — CC 42, MI 54.84, SLOC 197
  • high complexity — src/context/authContext.tsx — CC 41, MI 55.86, SLOC 266
  • high complexity — src/components/Home/EnableAutoBalanceBanner.tsx — CC 31, MI 59.11, SLOC 74
  • medium high-mdd — src/context/kernelClient.context.tsx:313 — KernelClientProvider: MDD 128.9 (uses across many lines from declarations)
  • medium high-mdd — src/context/authContext.tsx:72 — AuthProvider: MDD 103.1 (uses across many lines from declarations)
  • medium high-mdd — src/app/(setup)/setup/page.tsx:32 — SetupPageContent: MDD 96.9 (uses across many lines from declarations)
  • medium high-mdd — src/components/Setup/Views/SetupPasskey.tsx:43 — SetupPasskey: MDD 75.2 (uses across many lines from declarations)
  • medium high-mdd — src/hooks/useZeroDev.ts:71 — useZeroDev: MDD 72.4 (uses across many lines from declarations)
  • medium high-dlt — src/hooks/useZeroDev.ts:71 — useZeroDev: DLT 70 (calls 70 distinct functions — high context load)
  • medium high-dlt — src/app/(setup)/setup/page.tsx:32 — SetupPageContent: DLT 66 (calls 66 distinct functions — high context load)
  • medium high-dlt — src/context/kernelClient.context.tsx:313 — KernelClientProvider: DLT 63 (calls 63 distinct functions — high context load)
  • medium high-dlt — src/context/authContext.tsx:72 — AuthProvider: DLT 55 (calls 55 distinct functions — high context load)
  • medium high-dlt — src/hooks/useZeroDev.ts:83 — handleRegister: DLT 42 (calls 42 distinct functions — high context load)
  • medium high-mdd — src/components/Home/EnableAutoBalanceBanner.tsx:41 — EnableAutoBalanceBanner: MDD 39.6 (uses across many lines from declarations)
  • medium high-mdd — src/hooks/query/user.ts:25 — useUserQuery: MDD 34.6 (uses across many lines from declarations)

…and 43 more.

✅ Resolved (62)

  • src/context/kernelClient.context.tsx — CC 105, MI 57.17, SLOC 506
  • src/app/(setup)/setup/page.tsx — CC 63, MI 53.98, SLOC 255
  • src/utils/webauthn.utils.ts — CC 58, MI 54.02, SLOC 195
  • src/context/authContext.tsx — 53 commits, +338/-208 lines since 6 months ago
  • src/hooks/useZeroDev.ts — 51 commits, +636/-385 lines since 6 months ago
  • src/components/Setup/Views/SetupPasskey.tsx — CC 40, MI 55.19, SLOC 193
  • src/context/authContext.tsx — CC 37, MI 54.83, SLOC 247
  • src/components/Home/EnableAutoBalanceBanner.tsx — CC 30, MI 59.42, SLOC 72
  • src/context/kernelClient.context.tsx:313 — KernelClientProvider: MDD 116.2 (uses across many lines from declarations)
  • src/context/authContext.tsx:69 — AuthProvider: MDD 105.9 (uses across many lines from declarations)
  • src/app/(setup)/setup/page.tsx:30 — SetupPageContent: MDD 85.3 (uses across many lines from declarations)
  • src/components/Setup/Views/SetupPasskey.tsx:39 — SetupPasskey: MDD 74.9 (uses across many lines from declarations)
  • src/hooks/useZeroDev.ts:70 — useZeroDev: MDD 70.2 (uses across many lines from declarations)
  • src/hooks/useZeroDev.ts:70 — useZeroDev: DLT 70 (calls 70 distinct functions — high context load)
  • src/context/kernelClient.context.tsx:313 — KernelClientProvider: DLT 62 (calls 62 distinct functions — high context load)
  • src/app/(setup)/setup/page.tsx:30 — SetupPageContent: DLT 56 (calls 56 distinct functions — high context load)
  • src/context/authContext.tsx:69 — AuthProvider: DLT 53 (calls 53 distinct functions — high context load)
  • src/hooks/useZeroDev.ts:82 — handleRegister: DLT 43 (calls 43 distinct functions — high context load)
  • src/components/Home/EnableAutoBalanceBanner.tsx:41 — EnableAutoBalanceBanner: MDD 39.2 (uses across many lines from declarations)
  • src/components/Setup/Views/SetupPasskey.tsx:39 — SetupPasskey: DLT 35 (calls 35 distinct functions — high context load)

…and 42 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/utils/passkey-auth-capture.ts 0.0 7.4 +7.4
src/utils/login-session.ts 0.0 4.3 +4.3
src/context/kernelClient.context.tsx 13.2 13.8 +0.6
src/hooks/query/user.ts 11.4 10.7 -0.8
src/utils/native-auth-capture.ts 8.3 0.0 -8.3

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • ✅ unit: 6408 ran, 0 failed, 0 skipped, 1.7m

📊 Coverage (unit)

metric %
statements 75.7%
branches 61.8%
functions 69.8%
lines 76.6%
⏱ 10 slowest test cases
time test
🐢 9.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Network failure keeps loading while retries remain, then shows the generic error
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › User KYC not approved fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_SOURCE_OVER_MONTHLY_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_VOLUME_NEAR_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_RECENT_REFUND fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_USER_NOT_PROVISIONED fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › routes the KYC rejection on its wire code, and does not retry it
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › a refused idempotency key tells the user to scan again, not to contact support
3.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Going offline blames the connection, and reconnecting clears it for the recovered scan
3.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Scan that recovers on the retry lands on the payment screen, not an error
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Chip review — changes requested

The auth and kernel ownership changes are well covered, but the new wallet-readiness gate can trap active-card users in a non-dismissible modal.

Findings

  • BLOCKING · src/components/Home/EnableAutoBalanceBanner.tsx:111 · Keep an escape when wallet initialization fails
    When an ACTIVE card lacks hasWithdrawApproval and the primary kernel build exhausts its retries, isKernelClientReady stays false. This prevent-close modal then renders with Continue disabled; Skip for now is only added after a grant attempt, but this gate prevents that attempt, so the user cannot reach Home even though the provider intentionally keeps the session alive. Remove this readiness gate and let grant() await ensureClientForChain, or show an immediate retry/Skip action while readiness is false.

Checked clean

  • Verified the detached HEAD, exact merge base, trusted author, and dev base ref.
  • Traced web and native passkey token capture through ceremony commit, login hydration, and replacement-session query guards.
  • Checked kernel-client build sequencing, cache invalidation, lazy-chain readiness, and session-key grant ownership.
  • Checked setup initialization timeout, missing-step recovery, username-collision handling, and their focused tests.
  • Exact-head unit, typecheck, eslint, format, and related completed checks passed. Advisory ds-shots failed in its duplicate Next build after compilation without an actionable source diagnostic; native export and preview were still running when inspected.
  • Local Jest and TypeScript binaries were absent from the detached read-only worktree, so no duplicate local run was possible.

Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.

Third opinion by claude-opus: 0 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: cb3bbbd69986 · Context: repo, sibling-api, ci · Took 22m

Comment thread src/components/Home/EnableAutoBalanceBanner.tsx Outdated

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Chip review — no blocking findings — this is not an approval

Clean review: both prior wallet-initialization lockout findings are fixed, and the auth, session-hydration, credential-ownership, and setup-recovery changes introduce no actionable defect.

Checked clean

  • Pinned checkout, trusted author, PR metadata, dev base ref, exact base SHA, exact head SHA, and merge base all match the supplied review target.
  • Earlier P1 is FIXED: Continue is no longer disabled by wallet readiness, and a failed readiness wait reaches the existing retry/skip recovery without submitting an approval.
  • Earlier P2 is FIXED: wallet initialization remains awaitable, but the blocking modal keeps an enabled escape path before and after an initialization failure.
  • Passkey verify-token capture was traced through the API response contract, ceremony-scoped stash, web/native token persistence, invite acceptance, badge claims, and login hydration.
  • Session replacement was checked across query cancellation, AbortSignal handling, token/clear-epoch guards before and after JSON parsing, bounded retries, and timeout cleanup.
  • Kernel-client initialization was checked for disposed builds, superseded rebuilds, credential changes, cache invalidation, owner-address validation, readiness publication, and reconnect recovery.
  • Setup initialization was checked for missing steps, empty layout state, session and authenticator timeouts, late completions, unsupported-device/browser branches, retry reload, and support access.
  • Registration collision handling was checked against both API 409 sites, the post-availability-check race, ceremony cleanup, retry suppression, recovery UI, and analytics deduplication.
  • Security, adversarial, and slop passes found no additional actionable privilege, token-exposure, stale-signing, dead-code, or architecture-drift issue in the changed behavior.
  • Exact-head unit, typecheck, eslint, format, design-system lint, native export, analysis/build, preview, policy, and aggregate CI checks succeeded. The advisory ds-shots duplicate build exited during webpack without an actionable source diagnostic while the independent build paths passed.
  • Local tests were not repeated because the detached worktree has no installed Jest binary; exact-head unit CI completed successfully.

Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.

Third opinion by claude-opus: 0 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 6dd14c4de658 · Context: repo, api · Took 18m

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

🖼 Visual diff — 11 screens moved

19 of 68 shots changed · 49 identical · baseline fd63d01 → head 6dd14c4

worst % screen widths
52.17% avatar-picker 320, 430
8.69% identity-verification 320, 430
7.70% unverified 320, 430
5.18% kyc-action-required 320, 430
4.54% profile 320, 430
4.24% profile-edit 320, 430
2.47% hugo-long-username 320, 430
2.18% long-full-name 320, 430
0.07% add-money 320
0.07% badges 320
0.03% empty-accounts 430

job summary · before/after/diff images — artifact

Fixture screenshots, no backend. Advisory — this check never blocks a merge. Posted from the default branch by ds-shots-comment.yml; the report it renders is untrusted data.

@innolope-dev
innolope-dev merged commit b041aa6 into dev Sep 8, 2026
36 of 37 checks passed

This branch was successfully deployed

1 active deployment
Preview — 6dd14c4d Deployed Sep 8, 2026 by vercel[bot]
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.

1 participant