Repository navigation
Conversation
|
12859c9 to
d42c9b7
Compare
d42c9b7 to
da3a484
Compare
da3a484 to
a8786e0
Compare
f72f7b7 to
c8b6820
Compare
c8b6820 to
579c45e
Compare
| window.localStorage.setItem( | ||
| sessionRefreshStorageKey(), | ||
| Date.now() + refreshDelay, | ||
| ); |
There was a problem hiding this comment.
If localStorage is full, setItem throws after the cookie exchange succeeds. The catch block retries the HTTP request, but each successful retry hits the same storage error. waitForSession() never resolves, so queued uploads never start even though the browser has a valid cookie.
Treat the freshness hint as optional. A failed storage write should not prevent readiness from finishing or renewal from continuing.
There was a problem hiding this comment.
3 issues found across 9 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/units/compiler/state_js.test.mjs">
<violation number="1" location="tests/units/compiler/state_js.test.mjs:37">
P3: This mock returns a fresh expiry for every localStorage key, so the warm-transport test still passes if `state.js` reads a misspelled or unscoped refresh key. Return the expiry only for `reflex:ws://localhost:8000/_event:expires_at`.</violation>
</file>
<file name="packages/reflex-base/news/+session-cookie-bootstrap.feature.md">
<violation number="1" location="packages/reflex-base/news/+session-cookie-bootstrap.feature.md:1">
P3: This overstates isolation: the bootstrap still reads the shared legacy `sessionStorage['token']` key, so an existing token can be reused across backend URLs. Limit this claim to the newly scoped keys.</violation>
</file>
<file name="packages/reflex-base/src/reflex_base/.templates/web/utils/state.js">
<violation number="1" location="packages/reflex-base/src/reflex_base/.templates/web/utils/state.js:1296">
P2: A full localStorage quota makes this write throw after the server has installed the cookie. The catch retries without resolving `initialSessionExchange`, leaving waiting uploads blocked despite a valid cookie. Treat the freshness hint as best-effort.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| window.localStorage.setItem( | ||
| sessionRefreshStorageKey(), | ||
| Date.now() + refreshDelay, | ||
| ); |
There was a problem hiding this comment.
P2: A full localStorage quota makes this write throw after the server has installed the cookie. The catch retries without resolving initialSessionExchange, leaving waiting uploads blocked despite a valid cookie. Treat the freshness hint as best-effort.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/reflex-base/src/reflex_base/.templates/web/utils/state.js, line 1296:
<comment>A full localStorage quota makes this write throw after the server has installed the cookie. The catch retries without resolving `initialSessionExchange`, leaving waiting uploads blocked despite a valid cookie. Treat the freshness hint as best-effort.</comment>
<file context>
@@ -1117,13 +1269,66 @@ export const connect = async (
+ setToken(session.client_token);
+ const refreshDelay =
+ (session.refresh_in ?? session.expires_in / 2) * 1000;
+ window.localStorage.setItem(
+ sessionRefreshStorageKey(),
+ Date.now() + refreshDelay,
</file context>
| window.localStorage.setItem( | |
| sessionRefreshStorageKey(), | |
| Date.now() + refreshDelay, | |
| ); | |
| try { | |
| window.localStorage.setItem( | |
| sessionRefreshStorageKey(), | |
| Date.now() + refreshDelay, | |
| ); | |
| } catch { | |
| // The cookie exchange succeeded; the freshness hint is best-effort. | |
| } |
| setItem: (key, value) => storage.set(key, value), | ||
| }; | ||
| window.localStorage = { | ||
| getItem: () => (storedToken ? String(Date.now() + 3600000) : undefined), |
There was a problem hiding this comment.
P3: This mock returns a fresh expiry for every localStorage key, so the warm-transport test still passes if state.js reads a misspelled or unscoped refresh key. Return the expiry only for reflex:ws://localhost:8000/_event:expires_at.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/units/compiler/state_js.test.mjs, line 37:
<comment>This mock returns a fresh expiry for every localStorage key, so the warm-transport test still passes if `state.js` reads a misspelled or unscoped refresh key. Return the expiry only for `reflex:ws://localhost:8000/_event:expires_at`.</comment>
<file context>
@@ -27,11 +28,14 @@ async function setup({
setItem: (key, value) => storage.set(key, value),
};
+ window.localStorage = {
+ getItem: () => (storedToken ? String(Date.now() + 3600000) : undefined),
+ };
const document = new EventTarget();
</file context>
| getItem: () => (storedToken ? String(Date.now() + 3600000) : undefined), | |
| getItem: (key) => | |
| key === "reflex:ws://localhost:8000/_event:expires_at" && storedToken | |
| ? String(Date.now() + 3600000) | |
| : undefined, |
| @@ -0,0 +1 @@ | |||
| Establish browser sessions over the WebSocket while hydration begins, then persist them through HTTP-only cookies with automatic refresh. Browser storage is isolated for each backend application. Idle tabs renew their session cookies automatically; transient cookie-exchange failures retry without blocking WebSocket events, and queued uploads remain cancellable. | |||
There was a problem hiding this comment.
P3: This overstates isolation: the bootstrap still reads the shared legacy sessionStorage['token'] key, so an existing token can be reused across backend URLs. Limit this claim to the newly scoped keys.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/reflex-base/news/+session-cookie-bootstrap.feature.md, line 1:
<comment>This overstates isolation: the bootstrap still reads the shared legacy `sessionStorage['token']` key, so an existing token can be reused across backend URLs. Limit this claim to the newly scoped keys.</comment>
<file context>
@@ -0,0 +1 @@
+Establish browser sessions over the WebSocket while hydration begins, then persist them through HTTP-only cookies with automatic refresh. Browser storage is isolated for each backend application. Idle tabs renew their session cookies automatically; transient cookie-exchange failures retry without blocking WebSocket events, and queued uploads remain cancellable.
</file context>
| Establish browser sessions over the WebSocket while hydration begins, then persist them through HTTP-only cookies with automatic refresh. Browser storage is isolated for each backend application. Idle tabs renew their session cookies automatically; transient cookie-exchange failures retry without blocking WebSocket events, and queued uploads remain cancellable. | |
| Establish browser sessions over the WebSocket while hydration begins, then persist them through HTTP-only cookies with automatic refresh. New routing-token and freshness keys are scoped to each backend URL. Idle tabs renew their session cookies automatically; transient cookie-exchange failures retry without blocking WebSocket events, and queued uploads remain cancellable. |
Let hydration and ordinary events run while the socket-delivered session credential is exchanged for an HttpOnly cookie. Credentials are never written to browser storage; routing-token and freshness keys are scoped to the backend URL. Web Locks coordinate simultaneous cold tabs, and duplicate tabs adopt server-issued replacement tokens.
Idle timers and visibility restoration request fresh credentials over the authenticated socket. Failed HTTP exchanges retry with bounded backoff while readiness stays pending. Success releases waiting uploads and Enterprise HTTP helpers; teardown aborts the exchange and rejects readiness. When an existing cookie replaces the provisional token, readiness waits until the replacement socket connects.
Uploads reserve their cancellation controller before waiting, so cancellation works immediately. Waiting uploads do not block ordinary WebSocket events. Transport warmup for established sessions and CONNECT-packet hydration remain intact.
Depends on #7362; real browser and deployment coverage is in #7364. Enterprise helper compatibility is in reflex-dev/reflex-enterprise#240. Rebased on main
d02ba5409.Validation: all 13 frontend runtime/lifecycle wrapper tests pass, including the actual upload helper, idle renewal, retry recovery, cancellation, late responses after teardown, and cookie-replacement reconnects. The new failure/reconnect regressions fail before their fixes. Full assembled stack: 12,147 unit tests passed with 80.15% coverage.
Related to ENG-12919, ENG-9088.