Skip to content

Cache auth tokens outside per-request secure storage reads #530

Description

@jjoonleo

Problem

TokenInterceptor loads tokens from FlutterSecureStorage for every intercepted request, including every protected API request. Secure storage is relatively expensive and platform-backed, so hot request paths should not depend on repeated keychain/secure-storage reads when the token is already known in memory.

Evidence

  • lib/core/dio/interceptors/token_interceptor.dart calls tokenLocalDataSource.getToken() inside onRequest.
  • The interceptor then adds the access token to the Authorization header for the request.
  • lib/data/data_sources/token_local_data_source.dart implements getToken() by reading from FlutterSecureStorage.
  • getToken() may perform the current iOS-options read and then a legacy-options fallback read, so a single protected request can trigger multiple secure-storage reads before network dispatch.
  • Refresh flow also calls getToken() again.

Proposed direction

Introduce an in-memory token cache owned by the token data source or a small token/session service:

  • Populate the cache after sign-in, token refresh, and first successful secure-storage read.
  • Update the cache whenever storeTokens or storeAuthToken writes new token values.
  • Clear the cache on deleteToken and local sign-out.
  • Keep secure storage as the persistence layer, but avoid reading it on every request once the cache is warm.
  • Preserve the legacy-token migration behavior on first load.

Acceptance criteria

  • Protected requests use cached access tokens after the first successful token load.
  • storeTokens, storeAuthToken, and refresh-token success update both secure storage and the in-memory value.
  • deleteToken clears both secure storage and the in-memory value.
  • Missing-token behavior remains unchanged for protected and auth-exempt endpoints.
  • Add unit coverage proving repeated protected requests do not repeatedly hit secure storage after the cache is warm.

Source: Codex codebase audit on 2026-06-28.

Metadata

Metadata

Assignees

No one assigned

    Labels

    codex-readyCan be started independently by a Codex thread nowpriority: P2Medium priority production-readiness workproduction-readinessWork required before production releaserefactor

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions