Bug + UX: Seed phrase never shown after identity creation; replace with key safety screen #41

Closed
opened 2026-04-26 16:45:59 +00:00 by icub3d · 0 comments
Owner

Migrated from GitHub issue icub3d/decentcom#53
Original Author: @icub3d
Original Date: 2026-04-16T18:13:01Z


Bug + UX: Seed phrase never shown after identity creation; replace with key safety screen

Bug

After clicking "Create New Identity" the seed phrase screen (SeedPhrase.tsx) is never rendered. The root cause is a race condition in useIdentity.ts:

// useIdentity.ts — generateIdentity()
const info = await invoke<IdentityInfo>("generate_identity");
setHasIdentity(true);   // ← triggers App.tsx re-render immediately
setPublicKey(info.pubkey);
await identityRefresh();
return info;            // ← Setup.tsx receives this after it is already unmounted

Affected file: client/src/hooks/useIdentity.ts, client/src/pages/Setup.tsx, client/src/App.tsx

Proposed Fix + UX Redesign

Rather than patching the race condition in isolation, replace the current post-generation SeedPhrase screen with a Key Safety screen that is shown immediately after a new identity is created, before hasIdentity is set true in the parent. The screen makes the stakes clear and gives the user concrete actions to protect their key.

Key Safety Screen behaviour

  • Shown inline within Setup.tsx before calling onComplete() (so hasIdentity is not yet true in the parent and the component stays mounted)
  • Explains that losing access to their device without a backup means permanent identity loss — they will not be able to authenticate to any server
  • Offers two recovery options (either or both):
    1. Export backup file — triggers the native file-save dialog via key_export IPC (same flow as KeyExport.tsx) so they can save a .dckb file right now
    2. Copy / view seed phrase — shows the 24-word BIP39 mnemonic with a copy button
  • A "I understand, continue" button (or equivalent) that calls onComplete() to finish setup — ideally requiring the user to acknowledge they have saved at least one form of backup, though a simple confirm is acceptable to start

Fix to the race condition

Move setHasIdentity(true) to after the seed/safety screen is dismissed (i.e., inside onComplete), not inside generateIdentity. Alternatively, generateIdentity can return the IdentityInfo without updating hasIdentity — the identity check that follows onComplete will pick it up naturally.

Requirements

  • Fix the race condition so the post-generation screen is always displayed
  • Replace SeedPhrase.tsx with a Key Safety screen that surfaces both recovery options
  • Export backup: passphrase input + file-save dialog, identical to KeyExport.tsx (can embed or reuse the component)
  • Seed phrase: display the 24 words in a readable grid with a copy-to-clipboard button
  • A clear warning that loss of both backup and seed phrase means permanent identity loss
  • "Continue" / "I've saved my key" button that completes setup
  • The screen also appears when adding an additional account (addingAccount === true)
  • Existing SeedPhrase.tsx component can be removed or repurposed

Design

Component Changes

  • client/src/pages/Setup.tsx — add "safety" view; pass generated IdentityInfo into it; call onComplete() only from the safety screen's confirm button
  • client/src/pages/KeySafety.tsx (new) — safety screen combining seed phrase display and backup export
  • client/src/hooks/useIdentity.tsgenerateIdentity should not call setHasIdentity(true); let the caller drive state after onComplete
  • client/src/pages/SeedPhrase.tsx — can be deleted once KeySafety.tsx covers its functionality

No server or Tauri core changes required.

Task List

  • Fix generateIdentity in useIdentity.ts so it does not set hasIdentity = true before the safety screen is dismissed
  • Add "safety" view state to Setup.tsx, store the generated IdentityInfo in local state, render KeySafety with it
  • Create client/src/pages/KeySafety.tsx: warning copy, seed phrase grid with copy button, embedded export (passphrase + file save), and confirm button
  • Wire onComplete so it is only called after the user confirms from the safety screen
  • Delete SeedPhrase.tsx (or keep as a dumb display sub-component used by KeySafety)
  • Update / add tests for the new flow

Test List

  • Unit test: after clicking "Create New Identity", the Key Safety screen is rendered (not the main app shell)
  • Unit test: onComplete is not called until the user clicks confirm on the safety screen
  • Unit test: seed phrase words are displayed correctly
  • Unit test: copy button copies the seed phrase to clipboard
  • Manual test: create a new identity, export a .dckb backup from the safety screen, clear the keychain, import the backup, confirm the same pubkey is restored
  • Manual test: create a new identity, note the seed phrase from the safety screen, clear the keychain, import via seed phrase, confirm the same pubkey is restored

Open Questions

  • Should the confirm button be gated on the user having taken at least one backup action (exported file or copied phrase), or is a simple acknowledge-and-continue acceptable?
  • Should KeySafety.tsx be under pages/ or components/setup/?
**Migrated from GitHub issue icub3d/decentcom#53** **Original Author:** @icub3d **Original Date:** 2026-04-16T18:13:01Z --- # Bug + UX: Seed phrase never shown after identity creation; replace with key safety screen ## Bug After clicking "Create New Identity" the seed phrase screen (`SeedPhrase.tsx`) is never rendered. The root cause is a race condition in `useIdentity.ts`: ```ts // useIdentity.ts — generateIdentity() const info = await invoke<IdentityInfo>("generate_identity"); setHasIdentity(true); // ← triggers App.tsx re-render immediately setPublicKey(info.pubkey); await identityRefresh(); return info; // ← Setup.tsx receives this after it is already unmounted ``` **Affected file:** `client/src/hooks/useIdentity.ts`, `client/src/pages/Setup.tsx`, `client/src/App.tsx` ## Proposed Fix + UX Redesign Rather than patching the race condition in isolation, replace the current post-generation `SeedPhrase` screen with a **Key Safety screen** that is shown immediately after a new identity is created, before `hasIdentity` is set true in the parent. The screen makes the stakes clear and gives the user concrete actions to protect their key. ### Key Safety Screen behaviour - Shown inline within `Setup.tsx` before calling `onComplete()` (so `hasIdentity` is not yet true in the parent and the component stays mounted) - Explains that losing access to their device without a backup means **permanent identity loss** — they will not be able to authenticate to any server - Offers two recovery options (either or both): 1. **Export backup file** — triggers the native file-save dialog via `key_export` IPC (same flow as `KeyExport.tsx`) so they can save a `.dckb` file right now 2. **Copy / view seed phrase** — shows the 24-word BIP39 mnemonic with a copy button - A "I understand, continue" button (or equivalent) that calls `onComplete()` to finish setup — ideally requiring the user to acknowledge they have saved at least one form of backup, though a simple confirm is acceptable to start ### Fix to the race condition Move `setHasIdentity(true)` to *after* the seed/safety screen is dismissed (i.e., inside `onComplete`), not inside `generateIdentity`. Alternatively, `generateIdentity` can return the `IdentityInfo` without updating `hasIdentity` — the identity check that follows `onComplete` will pick it up naturally. ## Requirements - [x] Fix the race condition so the post-generation screen is always displayed - [x] Replace `SeedPhrase.tsx` with a Key Safety screen that surfaces both recovery options - [x] Export backup: passphrase input + file-save dialog, identical to `KeyExport.tsx` (can embed or reuse the component) - [x] Seed phrase: display the 24 words in a readable grid with a copy-to-clipboard button - [x] A clear warning that loss of both backup and seed phrase means permanent identity loss - [x] "Continue" / "I've saved my key" button that completes setup - [x] The screen also appears when adding an additional account (`addingAccount === true`) - [x] Existing `SeedPhrase.tsx` component can be removed or repurposed ## Design ### Component Changes - **`client/src/pages/Setup.tsx`** — add `"safety"` view; pass generated `IdentityInfo` into it; call `onComplete()` only from the safety screen's confirm button - **`client/src/pages/KeySafety.tsx`** (new) — safety screen combining seed phrase display and backup export - **`client/src/hooks/useIdentity.ts`** — `generateIdentity` should not call `setHasIdentity(true)`; let the caller drive state after `onComplete` - **`client/src/pages/SeedPhrase.tsx`** — can be deleted once `KeySafety.tsx` covers its functionality ### No server or Tauri core changes required. ## Task List - [x] Fix `generateIdentity` in `useIdentity.ts` so it does not set `hasIdentity = true` before the safety screen is dismissed - [x] Add `"safety"` view state to `Setup.tsx`, store the generated `IdentityInfo` in local state, render `KeySafety` with it - [x] Create `client/src/pages/KeySafety.tsx`: warning copy, seed phrase grid with copy button, embedded export (passphrase + file save), and confirm button - [x] Wire `onComplete` so it is only called after the user confirms from the safety screen - [x] Delete `SeedPhrase.tsx` (or keep as a dumb display sub-component used by `KeySafety`) - [x] Update / add tests for the new flow ## Test List - [x] Unit test: after clicking "Create New Identity", the Key Safety screen is rendered (not the main app shell) - [x] Unit test: `onComplete` is not called until the user clicks confirm on the safety screen - [x] Unit test: seed phrase words are displayed correctly - [x] Unit test: copy button copies the seed phrase to clipboard - [ ] Manual test: create a new identity, export a `.dckb` backup from the safety screen, clear the keychain, import the backup, confirm the same pubkey is restored - [ ] Manual test: create a new identity, note the seed phrase from the safety screen, clear the keychain, import via seed phrase, confirm the same pubkey is restored ## Open Questions - Should the confirm button be gated on the user having taken at least one backup action (exported file or copied phrase), or is a simple acknowledge-and-continue acceptable? - Should `KeySafety.tsx` be under `pages/` or `components/setup/`?
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
icub3d/decentcom#41
No description provided.