Problem
surfsense_web/components/TokenHandler.tsx writes the bearer token to localStorage twice in a row:
// surfsense_web/components/TokenHandler.tsx, lines 47–49
localStorage.setItem(storageKey, token); // <-- redundant
setBearerToken(token); // already does the same write + Electron sync
Looking at setBearerToken in surfsense_web/lib/auth-utils.ts:94:
export function setBearerToken(token: string): void {
if (typeof window === "undefined") return;
localStorage.setItem(BEARER_TOKEN_KEY, token); // BEARER_TOKEN_KEY === "surfsense_bearer_token"
syncTokensToElectron();
}
BEARER_TOKEN_KEY (auth-utils) and TokenHandler's default storageKey are both "surfsense_bearer_token".
- The only caller of
<TokenHandler> (surfsense_web/app/auth/callback/page.tsx:14) explicitly passes storageKey="surfsense_bearer_token" — same value.
- The first
localStorage.setItem(storageKey, token) writes to the same key setBearerToken writes to immediately after, but without triggering syncTokensToElectron.
Result: a redundant write that creates a brief inconsistent state and tempts "simplification" in the wrong direction. The token-write adapter is split across two lines.
Files
surfsense_web/components/TokenHandler.tsx (lines 47–49, props at 24–28)
surfsense_web/app/auth/callback/page.tsx (the only caller)
What to do
- In
TokenHandler.tsx, delete line 48 (localStorage.setItem(storageKey, token);). Keep setBearerToken(token) — it owns the write + Electron sync.
- Since the only caller passes the default value for
storageKey, also remove the storageKey prop entirely (it's no longer used). Update auth/callback/page.tsx to drop the prop.
- (Optional) Update the JSDoc on
TokenHandler to remove the now-stale storageKey reference.
- Test the OAuth callback flow end-to-end: Google sign-in → confirm bearer + refresh tokens land in
localStorage and (on desktop) propagate to Electron.
Why this matters
- Single token-write adapter: nobody has to wonder "which write wins?" or "why are we writing twice?"
- Removes a regression trap: a future contributor could remove
setBearerToken(...) thinking the raw setItem covers it — silently breaking Electron Quick Ask + Autocomplete windows.
- Smaller, deeper interface for
TokenHandler.
Acceptance criteria
Difficulty
Good first issue — single-file deletion with one prop cleanup.
Problem
surfsense_web/components/TokenHandler.tsxwrites the bearer token tolocalStoragetwice in a row:Looking at
setBearerTokeninsurfsense_web/lib/auth-utils.ts:94:BEARER_TOKEN_KEY(auth-utils) andTokenHandler's defaultstorageKeyare both"surfsense_bearer_token".<TokenHandler>(surfsense_web/app/auth/callback/page.tsx:14) explicitly passesstorageKey="surfsense_bearer_token"— same value.localStorage.setItem(storageKey, token)writes to the same keysetBearerTokenwrites to immediately after, but without triggeringsyncTokensToElectron.Result: a redundant write that creates a brief inconsistent state and tempts "simplification" in the wrong direction. The token-write adapter is split across two lines.
Files
surfsense_web/components/TokenHandler.tsx(lines 47–49, props at 24–28)surfsense_web/app/auth/callback/page.tsx(the only caller)What to do
TokenHandler.tsx, delete line 48 (localStorage.setItem(storageKey, token);). KeepsetBearerToken(token)— it owns the write + Electron sync.storageKey, also remove thestorageKeyprop entirely (it's no longer used). Updateauth/callback/page.tsxto drop the prop.TokenHandlerto remove the now-stalestorageKeyreference.localStorageand (on desktop) propagate to Electron.Why this matters
setBearerToken(...)thinking the rawsetItemcovers it — silently breaking Electron Quick Ask + Autocomplete windows.TokenHandler.Acceptance criteria
TokenHandler.tsxcallssetBearerTokenonly (no rawlocalStorage.setItemfor the bearer)storageKeyprop removed (or, if you'd rather keep it for backward compat, document why)auth/callback/page.tsxupdatedDifficulty
Good first issue — single-file deletion with one prop cleanup.