The frontend component of PuffPastry, meant to interface with the backend, which in turn interfaces with the smart contract.
Não pode escolher mais do que 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.
 
 
 
 
 

8.6 KiB

sessionId
session-261005-185830-1pry

Requirements

Overview & Goals

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.

Scope

  • In Scope:
    • Updating src/main.ts to configure OpenAPI.TOKEN with a dynamic token resolver function using getAuthToken() or localStorage.
    • Standardizing the token storage key across BrandBar.vue, src/utils/auth.ts, and runtime configurations.
    • Ensuring generated service requests (openapi-typescript-codegen client) dynamically attach the Authorization: Bearer <token> header on every HTTP request whenever a token is present.
    • Verifying clean execution and graceful handling when a user is unauthenticated or logs out.
  • Out of Scope:
    • Changing backend JWT signing, expiration, or validation algorithms.
    • Modifying gen-api.sh script (handled previously or independently).

User Stories

  • 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.
  • As a user logging in via Freighter wallet, I want my session JWT to take effect immediately without requiring a full page refresh.
  • 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.

Functional Requirements

  • 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.
  • Header Injection: Ensure src/generated/typescript/core/request.ts automatically attaches Authorization: Bearer <token> to request headers whenever token is non-empty.
  • Consistent Storage Keys: Standardize token retrieval through getAuthToken() from src/utils/auth.ts (which checks authToken and token fallbacks in localStorage).
  • 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.
  • 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.

Non-Functional Requirements

  • Performance: Zero overhead beyond a single synchronous/cached localStorage read per outgoing request.
  • Reliability & Type Safety: Preserve strict TypeScript types and compatibility with openapi-typescript-codegen generated core contracts.

Technical Design

Current Implementation

  1. src/main.ts: Currently sets static headers at module load time:

    OpenAPI.BASE = ''
    OpenAPI.HEADERS = localStorage.getItem("token") ? {"Authorization": "Bearer " + localStorage.getItem("token")} : {};
    

    Issues:

    • OpenAPI.HEADERS is evaluated once when the bundle loads. If the user logs in afterwards, OpenAPI.HEADERS remains {}.
    • It checks key "token", while BrandBar.vue saves to "authToken".
    • It leaves OpenAPI.TOKEN undefined, bypassing the built-in token resolver in request.ts.
  2. src/components/BrandBar.vue: Upon connecting Freighter wallet:

    const jwt = await AuthService.loginFreighter({stellarAddress: addr.address});
    try {
      localStorage.setItem("authToken", jwt.token as string);
    } catch (_) {}
    
  3. src/utils/auth.ts: Provides helper functions:

    export function getAuthToken(): string | null {
      try {
        return localStorage.getItem('authToken') || localStorage.getItem('token');
      } catch {
        return null;
      }
    }
    
  4. src/generated/typescript/core/request.ts: The generated client’s getHeaders() function natively supports dynamic token resolvers:

    const [token, username, password, additionalHeaders] = await Promise.all([
        resolve(options, config.TOKEN),
        resolve(options, config.USERNAME),
        resolve(options, config.PASSWORD),
        resolve(options, config.HEADERS),
    ]);
    ...
    if (isStringWithValue(token)) {
        headers['Authorization'] = `Bearer ${token}`;
    }
    

Key Decisions

  • 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.
  • 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.
  • Clean Up Redundant Imports and Static Headers: Remove the broken OpenAPI.HEADERS static assignment and duplicate ./index.css import in src/main.ts.

Architecture Diagram

graph LR
  User[User / Freighter Wallet] -->|Login| BrandBar[BrandBar.vue]
  BrandBar -->|AuthService.loginFreighter| Backend[Backend API]
  Backend -->|Return JWT Token| BrandBar
  BrandBar -->|localStorage.setItem authToken| Storage[(localStorage)]
  Services[Generated API Services\nProposal / Amendment / Comment] -->|__request| RequestCore[core/request.ts]
  RequestCore -->|Resolve OpenAPI.TOKEN| AuthUtil[utils/auth.ts: getAuthToken]
  AuthUtil -->|Read Token| Storage
  RequestCore -->|Headers with Authorization: Bearer token| Backend

Proposed Changes

  1. src/main.ts:

    • Import getAuthToken from ./utils/auth.
    • Set OpenAPI.TOKEN = async () => getAuthToken() || '' (or () => getAuthToken() ?? undefined).
    • Remove obsolete OpenAPI.HEADERS static assignment.
    • Clean up duplicate import './index.css'.
  2. src/utils/auth.ts:

    • Ensure getAuthToken() safely handles nulls and retrieves the active token.
  3. src/components/BrandBar.vue:

    • Ensure the token retrieved from AuthService.loginFreighter is consistently saved to localStorage under authToken.

File Structure

  • src/main.ts (modified: configure OpenAPI.TOKEN resolver)
  • src/components/BrandBar.vue (verified / modified: ensure consistent token persistence on login)
  • src/utils/auth.ts (verified / modified: token utility resolution)

Risks

  • Storage Availability: If local storage is disabled (e.g. private browsing restrictions), getAuthToken() returns null safely without unhandled exceptions.
  • Token Invalidation / Expiry: If a token expires, request.ts will receive standard 401 response from backend, allowing frontend components to handle re-authentication.

Testing

Validation Approach

Verify that outgoing HTTP requests dispatched via generated API services include the Authorization: Bearer <token> header when authenticated, and omit it when unauthenticated.

Key Scenarios

  1. Authenticated Requests After Login:
    • Connect Freighter wallet and login via BrandBar.vue.
    • Verify that subsequent calls to ProposalService.listProposals(), AmendmentService.listAmendments(), CommentService.addComment(), etc. include Authorization: Bearer <token> in the request headers.
  2. Initial App Load with Pre-existing Token:
    • With a valid authToken already stored in localStorage, refresh the page.
    • Verify that immediate API calls during initial mount include the bearer token.
  3. Unauthenticated Requests:
    • With empty localStorage, verify that public API requests execute without an Authorization header and without JavaScript runtime errors.

Edge Cases

  • Dynamic token change (e.g., logging in as a different user) is immediately reflected in the very next request without requiring application restart.
  • Null or whitespace token values do not produce malformed Authorization: Bearer headers.