Page MenuHomePhabricator

persistent login (!61)
Open, Needs TriagePublic

Description

the login is now lost on closing the page. lets make it more persistent. use IndexedDB for this client side. or beter more secure ways if they exist.
We use Client side OAuth in this tool.

Event Timeline

Daanvr moved this task from To do to Doing on the Tool-upload-workbench board.
Daanvr renamed this task from persistent login to persistent login (!61).Sun, May 17, 4:06 PM
Daanvr moved this task from Doing to Reviewing on the Tool-upload-workbench board.

MR: https://gitlab.wikimedia.org/daanvr/upload-workbench/-/merge_requests/61
Preview: https://upload-workbench.toolforge.org/mr-61/

Root cause

Not a missing-persistence problem — localStorage (which already held the refresh token) persists across tab close fine. The actual bug: in src/api/oauth.js, storeTokens() put the access token + expiry in sessionStorage (lines 58-66, since the initial scaffold commit d9610af), and isAuthenticated() reads that synchronously. On the next visit <AuthGate> calls isAuthenticated() → sessionStorage is empty → <Login/> renders, never giving getAccessToken()'s refresh-token fallback a chance to run. The long-lived refresh token in localStorage was effectively dead weight.

Storage choice — security analysis

The task asked for "IndexedDB or better more secure ways if they exist". For OAuth bearer tokens in an SPA with no backend:

  • localStorage — same-origin readable from any script. Vulnerable to XSS.
  • sessionStorage — same XSS surface as localStorage, doesn't persist across tab close. Worse than localStorage for this task's goal.
  • IndexedDB — same XSS surface (any same-origin script can indexedDB.open(...) and read). NOT meaningfully more secure than localStorage. Only quantitatively (slightly higher friction for naive attackers).
  • httpOnly cookies — genuinely more secure, but require a backend. We have none (PKCE public client). Out of scope.
  • WebCrypto + IndexedDB w/ user-derived key — defeats the seamless-login UX you asked for.

Conclusion: localStorageIndexedDB is a security wash for this threat model. Persistence was the bug; storage backend doesn't need to change.

Relationship to T426435

T426435 (Backlog) asks for a 30s expiry margin to fix a separate race — orthogonal to this fix (expiry timestamp is correct here, just stored in the wrong bucket). Can be picked up independently.