The frontend component of PuffPastry, meant to interface with the backend, which in turn interfaces with the smart contract.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

add-auth-header-to-gen-api.md 8.6 KiB

​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. ---
  2. sessionId: session-261005-185830-1pry
  3. ---
  4. # Requirements
  5. ### Overview & Goals
  6. The goal is to ensure that all generated API requests in the frontend application automatically and dynamically include the user's `Authorization: Bearer <token>` header once authenticated upon login. Currently, the OpenAPI configuration in `src/main.ts` statically evaluates `localStorage` once at application startup using an inconsistent key, preventing subsequent API calls made after login from being authenticated. By configuring `OpenAPI.TOKEN` with a dynamic token resolver in `src/main.ts` and standardizing token persistence across authentication flows, all generated API service calls (e.g. `ProposalService`, `AmendmentService`, `CommentService`, `AuthService`) will seamlessly include the active bearer token.
  7. ### Scope
  8. - **In Scope**:
  9. - Updating `src/main.ts` to configure `OpenAPI.TOKEN` with a dynamic token resolver function using `getAuthToken()` or `localStorage`.
  10. - Standardizing the token storage key across `BrandBar.vue`, `src/utils/auth.ts`, and runtime configurations.
  11. - Ensuring generated service requests (`openapi-typescript-codegen` client) dynamically attach the `Authorization: Bearer <token>` header on every HTTP request whenever a token is present.
  12. - Verifying clean execution and graceful handling when a user is unauthenticated or logs out.
  13. - **Out of Scope**:
  14. - Changing backend JWT signing, expiration, or validation algorithms.
  15. - Modifying `gen-api.sh` script (handled previously or independently).
  16. ### User Stories
  17. - As an authenticated user, I want all my API actions (such as voting, creating proposals, adding amendments, and posting comments) to automatically include my bearer token so that the backend can authenticate and authorize my operations.
  18. - As a user logging in via Freighter wallet, I want my session JWT to take effect immediately without requiring a full page refresh.
  19. - As a developer, I want the generated OpenAPI client to dynamically resolve the current auth token on each request so that authentication state remains reactive and reliable.
  20. ### Functional Requirements
  21. - **Dynamic Token Resolution**: Configure `OpenAPI.TOKEN` in `src/main.ts` as an asynchronous resolver function `async () => getAuthToken() || ''` (or `() => getAuthToken() ?? undefined`) so that `resolve(options, config.TOKEN)` evaluates per-request.
  22. - **Header Injection**: Ensure `src/generated/typescript/core/request.ts` automatically attaches `Authorization: Bearer <token>` to request headers whenever `token` is non-empty.
  23. - **Consistent Storage Keys**: Standardize token retrieval through `getAuthToken()` from `src/utils/auth.ts` (which checks `authToken` and `token` fallbacks in `localStorage`).
  24. - **Reactivity on Login**: When `BrandBar.vue` completes `AuthService.loginFreighter`, stored token in `localStorage` is immediately picked up by subsequent API requests without reloading the page.
  25. - **Safe Fallback for Unauthenticated Requests**: When no token exists in storage, `OpenAPI.TOKEN` resolves to empty/undefined and requests are sent without `Authorization` header, preventing malformed headers or runtime errors.
  26. ### Non-Functional Requirements
  27. - **Performance**: Zero overhead beyond a single synchronous/cached `localStorage` read per outgoing request.
  28. - **Reliability & Type Safety**: Preserve strict TypeScript types and compatibility with `openapi-typescript-codegen` generated core contracts.
  29. # Technical Design
  30. ### Current Implementation
  31. 1. **`src/main.ts`**:
  32. Currently sets static headers at module load time:
  33. ```typescript
  34. OpenAPI.BASE = ''
  35. OpenAPI.HEADERS = localStorage.getItem("token") ? {"Authorization": "Bearer " + localStorage.getItem("token")} : {};
  36. ```
  37. **Issues**:
  38. - `OpenAPI.HEADERS` is evaluated once when the bundle loads. If the user logs in afterwards, `OpenAPI.HEADERS` remains `{}`.
  39. - It checks key `"token"`, while `BrandBar.vue` saves to `"authToken"`.
  40. - It leaves `OpenAPI.TOKEN` `undefined`, bypassing the built-in token resolver in `request.ts`.
  41. 2. **`src/components/BrandBar.vue`**:
  42. Upon connecting Freighter wallet:
  43. ```typescript
  44. const jwt = await AuthService.loginFreighter({stellarAddress: addr.address});
  45. try {
  46. localStorage.setItem("authToken", jwt.token as string);
  47. } catch (_) {}
  48. ```
  49. 3. **`src/utils/auth.ts`**:
  50. Provides helper functions:
  51. ```typescript
  52. export function getAuthToken(): string | null {
  53. try {
  54. return localStorage.getItem('authToken') || localStorage.getItem('token');
  55. } catch {
  56. return null;
  57. }
  58. }
  59. ```
  60. 4. **`src/generated/typescript/core/request.ts`**:
  61. The generated client's `getHeaders()` function natively supports dynamic token resolvers:
  62. ```typescript
  63. const [token, username, password, additionalHeaders] = await Promise.all([
  64. resolve(options, config.TOKEN),
  65. resolve(options, config.USERNAME),
  66. resolve(options, config.PASSWORD),
  67. resolve(options, config.HEADERS),
  68. ]);
  69. ...
  70. if (isStringWithValue(token)) {
  71. headers['Authorization'] = `Bearer ${token}`;
  72. }
  73. ```
  74. ### Key Decisions
  75. - **Use `OpenAPI.TOKEN` Resolver Function**: Instead of assigning static headers or managing manual interceptors, leverage `openapi-typescript-codegen`'s native `TOKEN` resolver `OpenAPI.TOKEN = async () => getAuthToken() || ''`. This ensures zero architectural changes to generated services and automatic per-request resolution.
  76. - **Centralize Token Lookup in `src/utils/auth.ts`**: Use `getAuthToken()` in `src/main.ts` to maintain unified support for both `authToken` and `token` storage keys and graceful exception handling if storage is restricted.
  77. - **Clean Up Redundant Imports and Static Headers**: Remove the broken `OpenAPI.HEADERS` static assignment and duplicate `./index.css` import in `src/main.ts`.
  78. ### Architecture Diagram
  79. ```mermaid
  80. graph LR
  81. User[User / Freighter Wallet] -->|Login| BrandBar[BrandBar.vue]
  82. BrandBar -->|AuthService.loginFreighter| Backend[Backend API]
  83. Backend -->|Return JWT Token| BrandBar
  84. BrandBar -->|localStorage.setItem authToken| Storage[(localStorage)]
  85. Services[Generated API Services\nProposal / Amendment / Comment] -->|__request| RequestCore[core/request.ts]
  86. RequestCore -->|Resolve OpenAPI.TOKEN| AuthUtil[utils/auth.ts: getAuthToken]
  87. AuthUtil -->|Read Token| Storage
  88. RequestCore -->|Headers with Authorization: Bearer token| Backend
  89. ```
  90. ### Proposed Changes
  91. 1. **`src/main.ts`**:
  92. - Import `getAuthToken` from `./utils/auth`.
  93. - Set `OpenAPI.TOKEN = async () => getAuthToken() || ''` (or `() => getAuthToken() ?? undefined`).
  94. - Remove obsolete `OpenAPI.HEADERS` static assignment.
  95. - Clean up duplicate `import './index.css'`.
  96. 2. **`src/utils/auth.ts`**:
  97. - Ensure `getAuthToken()` safely handles nulls and retrieves the active token.
  98. 3. **`src/components/BrandBar.vue`**:
  99. - Ensure the token retrieved from `AuthService.loginFreighter` is consistently saved to `localStorage` under `authToken`.
  100. ### File Structure
  101. - `src/main.ts` (modified: configure `OpenAPI.TOKEN` resolver)
  102. - `src/components/BrandBar.vue` (verified / modified: ensure consistent token persistence on login)
  103. - `src/utils/auth.ts` (verified / modified: token utility resolution)
  104. ### Risks
  105. - **Storage Availability**: If local storage is disabled (e.g. private browsing restrictions), `getAuthToken()` returns `null` safely without unhandled exceptions.
  106. - **Token Invalidation / Expiry**: If a token expires, `request.ts` will receive standard 401 response from backend, allowing frontend components to handle re-authentication.
  107. # Testing
  108. ### Validation Approach
  109. Verify that outgoing HTTP requests dispatched via generated API services include the `Authorization: Bearer <token>` header when authenticated, and omit it when unauthenticated.
  110. ### Key Scenarios
  111. 1. **Authenticated Requests After Login**:
  112. - Connect Freighter wallet and login via `BrandBar.vue`.
  113. - Verify that subsequent calls to `ProposalService.listProposals()`, `AmendmentService.listAmendments()`, `CommentService.addComment()`, etc. include `Authorization: Bearer <token>` in the request headers.
  114. 2. **Initial App Load with Pre-existing Token**:
  115. - With a valid `authToken` already stored in `localStorage`, refresh the page.
  116. - Verify that immediate API calls during initial mount include the bearer token.
  117. 3. **Unauthenticated Requests**:
  118. - With empty `localStorage`, verify that public API requests execute without an `Authorization` header and without JavaScript runtime errors.
  119. ### Edge Cases
  120. - Dynamic token change (e.g., logging in as a different user) is immediately reflected in the very next request without requiring application restart.
  121. - Null or whitespace token values do not produce malformed `Authorization: Bearer ` headers.