The frontend component of PuffPastry, meant to interface with the backend, which in turn interfaces with the smart contract.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

persist-wallet-data-use-session.md 13 KiB

​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. ---
  2. sessionId: session-261005-211542-1soe
  3. ---
  4. # Requirements
  5. ### Overview & Goals
  6. To maximize application utility and deliver an optimal user experience, users should not have to manually reconnect their Freighter wallet or re-authenticate after every browser refresh. The objective is to enhance `src/composables/useSession.ts` to manage and persist wallet connection data and authentication tokens across page reloads, making session state globally available and reactive across all UI components.
  7. ### Scope
  8. - **In Scope**:
  9. - Expanding `src/composables/useSession.ts` into a reactive session composable managing wallet address, user ID, auth token, and connection lifecycle.
  10. - Hydrating wallet and authentication state from `localStorage` on initial application load and browser refresh.
  11. - Providing centralized methods in `useSession.ts` for connecting, disconnecting, checking Freighter status, and formatting wallet displays.
  12. - Refactoring `src/components/BrandBar.vue` and `src/App.vue` to leverage `useSession()` for unified session state management.
  13. - Ensuring outgoing API requests via OpenAPI client continue to resolve persisted tokens seamlessly.
  14. - **Out of Scope**:
  15. - Modifying backend authentication endpoints or token validation mechanisms.
  16. - Modifying Freighter browser extension APIs or Stellar SDK internal logic.
  17. ### User Stories
  18. - As a platform user, I want my wallet connection and authentication status to persist when I refresh the page so that I do not need to repeatedly click "Connect Wallet" and approve popups.
  19. - As a user, I want the header brand bar to immediately reflect my connected wallet address on page load so that I know my active session is valid.
  20. - As a developer, I want a single source of truth for wallet session state in `useSession.ts` so that components across the application can reliably access authentication details without duplicate storage or Freighter queries.
  21. ### Functional Requirements
  22. - **Automatic State Hydration**: On application mount or composable initialization, `useSession` checks `localStorage` for existing `authToken` and cached wallet address, verifies Freighter extension availability, and synchronizes reactive state.
  23. - **Unified Connect Flow**: `connectWallet()` requests Freighter permission (`setAllowed()`), retrieves the public key (`getAddress()`), logs in via `AuthService.loginFreighter`, stores the token in `localStorage`, and updates reactive state.
  24. - **Clean Disconnect Flow**: `disconnectWallet()` removes authentication and wallet data from `localStorage` and resets reactive variables to unauthenticated state.
  25. - **Reactive UI Synchronization**: `BrandBar.vue` dynamically displays either "Connect Wallet" or the truncated address based on `useSession` reactive state.
  26. ### Non-Functional Requirements
  27. - **Resilience & Fault Tolerance**: If storage is corrupted, inaccessible, or the JWT is invalid/expired, `useSession` safely resets state without throwing unhandled exceptions.
  28. - **Performance**: Hydration must occur synchronously/microtask-level during initial render so UI does not flicker between unauthenticated and authenticated states.
  29. # Technical Design
  30. ### Current Implementation
  31. - `src/composables/useSession.ts` is currently a minimal stub containing only `hasFreighter = async () => freighter.isConnected()`.
  32. - `src/components/BrandBar.vue` independently executes Freighter login, sets `localStorage.setItem("authToken", ...)`, and updates a local component ref `userText`. When the user refreshes, this component state is lost and resets to `'Connect Wallet'`.
  33. - `src/App.vue` calls `freighter.getAddress()` in `onBeforeMount` and stores it in an isolated local ref `address`.
  34. - `src/utils/auth.ts` provides `getAuthToken()`, `parseJwt()`, and `getUserIdFromJwt()`, retrieving tokens from `localStorage`.
  35. ### Key Decisions
  36. - **Shared Module-Level Reactive State in `useSession.ts`**:
  37. - *Decision*: Maintain `walletAddress`, `authToken`, `userId`, `isConnecting`, and `isInitialized` as module-level `ref`s within `useSession.ts` (consistent with patterns used in `useModal.ts` and `useLoading.ts`).
  38. - *Rationale*: Guarantees all consumer components share the exact same reactive session state without requiring redundant Pinia store overhead or desynchronized state copies.
  39. - **Storage Strategy**:
  40. - *Decision*: Persist `authToken` under the standard `'authToken'` key in `localStorage` and derive/cache `stellarAddress` and `userId` directly from JWT payload parsing (`parseJwt`) and Freighter sync.
  41. - *Rationale*: Keeps `localStorage` minimal, avoids stale divergent data, and maintains full compatibility with `src/main.ts` (`OpenAPI.TOKEN`) and `src/utils/auth.ts`.
  42. - **Eager Hydration & Verification**:
  43. - *Decision*: Restore state immediately from `localStorage` on initial access, followed by asynchronous verification with Freighter (`freighter.isAllowed()`).
  44. - *Rationale*: Eliminates UI layout shifts or "flash of unauthenticated content" while still ensuring the browser extension remains connected.
  45. ### Proposed Changes
  46. 1. **`src/composables/useSession.ts`**:
  47. - Add reactive state: `walletAddress: ref<string | null>`, `authToken: ref<string | null>`, `userId: ref<number | undefined>`, `isConnecting: ref<boolean>`.
  48. - Add computed helpers: `isAuthenticated: computed(() => !!authToken.value && !!walletAddress.value)`, `truncatedAddress: computed(() => walletAddress.value ? truncateWallet(walletAddress.value) : '')`.
  49. - Implement `initSession()` to restore token from `localStorage`, decode JWT for `stellarAddress` / `userId`, and verify with Freighter if available.
  50. - Implement `connectWallet()` and `disconnectWallet()`.
  51. - Implement `hasFreighter()` to check extension availability.
  52. 2. **`src/components/BrandBar.vue`**:
  53. - Replace local `userText` logic with `const { isAuthenticated, truncatedAddress, isConnecting, connectWallet, disconnectWallet } = useSession()`.
  54. - Compute button text: show `truncatedAddress` if authenticated, `"Connecting..."` if connecting, or `"Connect Wallet"`.
  55. 3. **`src/App.vue`**:
  56. - Initialize session via `initSession()` in `onBeforeMount` / `onMounted`.
  57. - Remove isolated, unused `address` ref.
  58. ### Data Models / Contracts
  59. ```typescript
  60. export interface SessionState {
  61. walletAddress: string | null;
  62. authToken: string | null;
  63. userId: number | undefined;
  64. isAuthenticated: boolean;
  65. isConnecting: boolean;
  66. hasFreighter: () => Promise<boolean>;
  67. connectWallet: () => Promise<boolean>;
  68. disconnectWallet: () => void;
  69. initSession: () => Promise<void>;
  70. truncatedAddress: string;
  71. }
  72. ```
  73. ### Components
  74. - `src/composables/useSession.ts`: Core session state, persistence, and wallet connection management.
  75. - `src/components/BrandBar.vue`: Primary consumer for wallet connect button and truncated address display.
  76. - `src/App.vue`: Top-level application shell invoking session initialization during application bootstrap.
  77. - `src/utils/auth.ts`: Helper utilities utilized by `useSession.ts` for token resolution and JWT decoding.
  78. ### File Structure
  79. - `src/composables/useSession.ts` (Modified: add full reactive session management and local persistence)
  80. - `src/components/BrandBar.vue` (Modified: wire to `useSession`)
  81. - `src/App.vue` (Modified: initialize session on mount and clean up duplicate state)
  82. ### Architecture Diagram
  83. ```mermaid
  84. graph LR
  85. subgraph UI Layer
  86. BB[BrandBar.vue]
  87. APP[App.vue]
  88. MOD[Proposal/Amendment Modals]
  89. end
  90. subgraph State & Session
  91. US[useSession.ts Composable]
  92. AUTH[src/utils/auth.ts]
  93. end
  94. subgraph External & Storage
  95. LS[(localStorage)]
  96. FR[Freighter Wallet Extension]
  97. API[AuthService / Backend]
  98. end
  99. APP -->|initSession| US
  100. BB -->|connect / display| US
  101. MOD -->|read wallet / auth| US
  102. US -->|read / write token| LS
  103. US -->|parse JWT| AUTH
  104. US -->|getAddress / setAllowed| FR
  105. US -->|loginFreighter| API
  106. ```
  107. ### Risks
  108. - **Freighter Extension Locked / Permission Denied**: If a user locks their Freighter wallet or declines permission, `useSession` will catch the error, log a warning, and keep or revert the state cleanly without crashing the UI.
  109. - **Corrupted Token in Storage**: If `localStorage` contains an invalid JWT string, `parseJwt()` returns `null`, prompting `useSession` to clear the bad token and reset state.
  110. # Testing
  111. ### Validation Approach
  112. Verify that the user's wallet connection and JWT authentication remain intact and reactive across page refreshes, and that connection/disconnection lifecycles function properly without regressions in API request authorization.
  113. ### Key Scenarios
  114. 1. **Wallet Connection & Persistence Across Page Reload**:
  115. - Click "Connect Wallet" in `BrandBar.vue` and approve connection.
  116. - Verify that the button updates to the truncated wallet address and `authToken` is saved in `localStorage`.
  117. - Refresh the browser (F5 / full reload).
  118. - Verify that on page load, `BrandBar.vue` immediately displays the truncated wallet address without requiring the user to click "Connect Wallet" again.
  119. 2. **Authenticated API Calls After Reload**:
  120. - With an existing persisted session, refresh the browser and navigate to proposal details or submit a comment/amendment.
  121. - Verify that outgoing requests include the `Authorization: Bearer <token>` header without authentication failure.
  122. 3. **Session Disconnection / Logout**:
  123. - Trigger disconnect (or simulate logout).
  124. - Verify that `localStorage` items are cleared, reactive state resets, and `BrandBar.vue` updates to "Connect Wallet".
  125. - Refresh the page and confirm the unauthenticated state remains.
  126. ### Edge Cases
  127. - **Page Load with No Token**: Initial load with empty `localStorage` renders "Connect Wallet" cleanly with no errors.
  128. - **Freighter Extension Not Installed**: `hasFreighter()` returns `false` and clicking connect displays a descriptive message rather than throwing unhandled rejections.
  129. - **Expired / Malformed Token**: Corrupted token in `localStorage` is safely purged during hydration.
  130. # Delivery Steps
  131. ### ✓ Step 1: Implement reactive wallet session state and persistence in useSession.ts
  132. `useSession.ts` provides a centralized reactive store for wallet address, authentication token, user ID, and connection status, with automatic local persistence and hydration.
  133. - Define reactive module-level state variables (`walletAddress`, `authToken`, `userId`, `isConnecting`, `isInitialized`) to maintain shared session state across all consumer components.
  134. - Implement `initSession()` to restore saved credentials (`authToken` and `walletAddress`) from `localStorage`, validate with `parseJwt()`, and verify Freighter extension availability via `freighter.isConnected()` and `freighter.isAllowed()`.
  135. - Implement `connectWallet()` to orchestrate permission request via `freighter.setAllowed()`, fetch public address via `freighter.getAddress()`, execute `AuthService.loginFreighter`, store `authToken` and `walletAddress` in `localStorage`, and update reactive session state.
  136. - Implement `disconnectWallet()` to clear stored tokens and wallet data from `localStorage` and reset reactive state.
  137. - Expose helper computed properties and functions including `isAuthenticated`, `truncatedAddress`, `hasFreighter`, and connection status flags.
  138. ### ✓ Step 2: Integrate useSession into BrandBar.vue and App.vue
  139. `BrandBar.vue` and `App.vue` consume `useSession.ts`, automatically restoring and displaying the active wallet address upon reload without user intervention.
  140. - Update `App.vue` on-mount lifecycle to invoke `initSession()` from `useSession`, ensuring wallet and auth state are restored immediately during startup.
  141. - Refactor `BrandBar.vue` to bind wallet button text and connection trigger directly to `useSession`'s `isAuthenticated`, `truncatedAddress`, `isConnecting`, and `connectWallet` / `disconnectWallet`.
  142. - Remove redundant direct Freighter API invocations in `BrandBar.vue` and `App.vue` in favor of centralized composable methods.
  143. - Ensure smooth fallback UI handling when Freighter extension is not installed or permissions are revoked.
  144. ### ✓ Step 3: Validate wallet persistence across page refreshes and edge cases
  145. The complete wallet persistence flow is verified across reloads, active voting/proposal actions, and logout scenarios.
  146. - Test connecting a Freighter wallet and refreshing the browser: confirm that wallet address, truncated display text, and JWT bearer token are immediately retained.
  147. - Verify that authenticated API requests dispatched through generated service clients (`CommentService`, `ProposalService`, `AmendmentService`) continue to attach the bearer token after page reload.
  148. - Test edge cases such as corrupted `localStorage` tokens, expired JWTs, Freighter wallet disconnection, and missing browser extensions to ensure graceful fallback without runtime errors.