Fix settings API client storage contract#35
Conversation
Co-authored-by: up2itnow0822 <up2itnow0822@gmail.com>
| export function getAuthToken(): string | null { | ||
| if (typeof window === 'undefined') return null; | ||
|
|
||
| return ( | ||
| window.sessionStorage.getItem(AUTH_TOKEN_KEY) || | ||
| window.localStorage.getItem(AUTH_TOKEN_KEY) | ||
| ); | ||
| } | ||
|
|
||
| export function persistSessionAuthToken(token: string): void { | ||
| if (typeof window === 'undefined') return; | ||
|
|
||
| const value = token.trim(); | ||
| if (value) { | ||
| window.sessionStorage.setItem(AUTH_TOKEN_KEY, value); | ||
| } |
There was a problem hiding this comment.
Legacy localStorage token re-emerges in new tabs / new sessions
persistSessionAuthToken writes only to sessionStorage, but getAuthToken falls back to localStorage when sessionStorage is empty. Because sessionStorage is per-tab and cleared on new sessions, a user who saves a new token via Settings will find that token applied only in the current tab — any new tab, or any browser restart, causes getAuthToken to silently return the old localStorage token instead. The URL save path avoids this by writing all three localStorage keys at once; the token path has no equivalent cleanup.
At minimum, writing a non-empty session token should also evict the legacy localStorage entry so it cannot resurface.
| export function persistSessionAuthToken(token: string): void { | ||
| if (typeof window === 'undefined') return; | ||
|
|
||
| const value = token.trim(); | ||
| if (value) { | ||
| window.sessionStorage.setItem(AUTH_TOKEN_KEY, value); | ||
| } | ||
| } |
There was a problem hiding this comment.
No way to revoke a session token via the Settings UI —
persistSessionAuthToken('') is a complete no-op. Once a user saves a token they cannot clear it through Settings; they must manually open DevTools to remove it. Adding an else branch to remove both the sessionStorage and legacy localStorage entries when the value is empty would give users a way to revoke the token.
| export function persistSessionAuthToken(token: string): void { | |
| if (typeof window === 'undefined') return; | |
| const value = token.trim(); | |
| if (value) { | |
| window.sessionStorage.setItem(AUTH_TOKEN_KEY, value); | |
| } | |
| } | |
| export function persistSessionAuthToken(token: string): void { | |
| if (typeof window === 'undefined') return; | |
| const value = token.trim(); | |
| if (value) { | |
| window.sessionStorage.setItem(AUTH_TOKEN_KEY, value); | |
| // Evict any legacy localStorage token so it cannot resurface in new tabs/sessions. | |
| window.localStorage.removeItem(AUTH_TOKEN_KEY); | |
| } else { | |
| window.sessionStorage.removeItem(AUTH_TOKEN_KEY); | |
| window.localStorage.removeItem(AUTH_TOKEN_KEY); | |
| } | |
| } |
| return ( | ||
| window.sessionStorage.getItem(AUTH_TOKEN_KEY) || | ||
| window.localStorage.getItem(AUTH_TOKEN_KEY) | ||
| ); |
There was a problem hiding this comment.
|| treats an empty-string sessionStorage value as missing
sessionStorage.getItem returns null when the key is absent and '' when the key exists with an empty string. Using || means an empty-string value silently falls through to the localStorage fallback. Using ?? (nullish coalescing) restricts the fallback to the null case only, which is the actual "key not set" condition.
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
Agent-OS PR Cleanup — Draft Assessment
PR #35 is the most complete of six overlapping draft PRs (#30–#35) that all target frontend settings/API client persistence.
What this PR does
- Introduces
apiConfig.tsas a single source of truth for backend URL and auth token - Migrates legacy localStorage keys (
api_url,backend_base_url,agentnexus_backend_url) - Keeps auth tokens session-scoped in memory (with legacy localStorage fallback)
- Adds unit tests (
apiConfig.test.ts) — all 4 pass locally
Consolidation recommendation
Close duplicate drafts #30–#34 after merging this PR. They solve the same problem with partial/incremental approaches.
Local verification
cd frontend && pnpm install --frozen-lockfile && pnpm exec jest src/lib/apiConfig.test.ts
# 4 passed
Ready for review once marked non-draft.
Sent by Cursor Automation: Untitled


Bug and impact
Settings saved backend URLs under keys the API client did not read, and auth tokens entered in Settings were not written to the session storage path used by requests. Custom backend deployments continued using the default API URL, and authenticated flows such as executions/builder calls could 401 after users saved a token.
Root cause
Recent settings hardening renamed/disconnected storage keys while
frontend/src/lib/api.tsstill read onlyapi_urlandauth_tokenfrom localStorage.Fix
viemto satisfy the existing wagmi import surface and restore production build.Validation
pnpm --filter @agentnexus/frontend test -- apiConfig.test.ts --runInBandpnpm --filter @agentnexus/frontend type-checkpnpm --filter @agentnexus/frontend buildGreptile Summary
This PR centralises storage for the API base URL and auth token into a shared
apiConfig.tshelper, then wires both Settings surfaces and the axios client to it. It also bumpsviemto^2.52.2to unblock the production build.persistApiBaseUrlwrites all three legacy localStorage keys atomically, sogetApiBaseUrlalways finds the saved value regardless of which key the old code used.persistSessionAuthTokenwrites only tosessionStorage, butgetAuthTokenfalls back tolocalStorage. BecausesessionStorageis scoped to a single tab and cleared on new sessions, a legacyauth_tokenvalue inlocalStoragewill silently resurface whenever the user opens a new tab or restarts the browser — even after the user has saved a different token through Settings.persistSessionAuthToken('')does nothing, so there is also no UI path to revoke an active or legacy token.Confidence Score: 3/5
The URL-routing fix is safe to ship; the token-handling path has a gap where the old localStorage token can silently take over in new tabs or sessions.
The core URL fix is solid and properly unifies the three legacy keys. The token handling writes the new value only to sessionStorage while leaving any pre-existing localStorage token untouched and readable as a fallback. A user who saves a new token via Settings will silently revert to their old token the moment they open a new tab or restart the browser — the exact authentication regression the PR aims to prevent. There is also no way to fully revoke a token through the Settings UI.
frontend/src/lib/apiConfig.ts — the persistSessionAuthToken / getAuthToken pair needs a localStorage eviction step and a clear-on-empty branch before the token fix is complete.
Important Files Changed
Sequence Diagram
sequenceDiagram participant User participant SettingsUI as Settings UI (page / modal) participant apiConfig as apiConfig.ts participant LS as localStorage participant SS as sessionStorage participant ApiClient as api.ts (axios) Note over User,ApiClient: Save backend URL User->>SettingsUI: Enter URL + click Save SettingsUI->>apiConfig: persistApiBaseUrl(url) apiConfig->>LS: setItem(api_url, url) apiConfig->>LS: setItem(backend_base_url, url) apiConfig->>LS: setItem(agentnexus_backend_url, url) Note over User,ApiClient: Save auth token User->>SettingsUI: Enter token + click Save SettingsUI->>apiConfig: persistSessionAuthToken(token) apiConfig->>SS: setItem(auth_token, token) Note right of LS: legacy auth_token in LS never cleared Note over User,ApiClient: Outgoing API request ApiClient->>apiConfig: getApiBaseUrl() apiConfig->>LS: getItem(api_url) → url ApiClient->>apiConfig: getAuthToken() apiConfig->>SS: getItem(auth_token) alt sessionStorage has token SS-->>apiConfig: session-token else sessionStorage empty (new tab / new session) apiConfig->>LS: getItem(auth_token) LS-->>apiConfig: legacy-token end apiConfig-->>ApiClient: token ApiClient->>ApiClient: set Authorization headerReviews (1): Last reviewed commit: "fix: reconnect settings to API client" | Re-trigger Greptile