Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
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.

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { parseJson } from "$/utils/helpers/json";
* @param refs The refs object to store the abort controller in.
* @param getBackendURL Function to get the backend URL.
* @param getToken Function to get the Reflex token.
* @param waitForSession Wait for the session cookie before starting HTTP traffic.
*
* @returns The response from posting to the UPLOADURL endpoint.
*/
Expand All @@ -28,6 +29,7 @@ export const uploadFiles = async (
refs,
getBackendURL,
getToken,
waitForSession,
) => {
files = files ?? [];

Expand All @@ -38,6 +40,29 @@ export const uploadFiles = async (
return false;
}

const controller = new AbortController();
refs[upload_ref_name] = controller;
if (waitForSession) {
let abortWaiting;
const cancelled = new Promise((resolve) => {
abortWaiting = resolve;
controller.signal.addEventListener("abort", abortWaiting, { once: true });
});
try {
await Promise.race([waitForSession(), cancelled]);
} catch (error) {
console.log("Upload session error:", error.message);
delete refs[upload_ref_name];
return false;
} finally {
controller.signal.removeEventListener("abort", abortWaiting);
}
}
if (controller.signal.aborted || !socket.connected) {
delete refs[upload_ref_name];
return false;
Comment thread
masenf marked this conversation as resolved.
}

// Track how many partial updates have been processed for this upload.
let resp_idx = 0;
const eventHandler = (progressEvent) => {
Expand Down Expand Up @@ -73,7 +98,6 @@ export const uploadFiles = async (
});
};

const controller = new AbortController();
const formdata = new FormData();

// Bound handler args are sent as a JSON form field that must precede the
Expand All @@ -89,7 +113,6 @@ export const uploadFiles = async (
});

// Send the file to the server.
refs[upload_ref_name] = controller;

return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
Expand Down Expand Up @@ -154,6 +177,7 @@ export const uploadFiles = async (

// Configure and send request
xhr.open("POST", getBackendURL(env.UPLOAD));
xhr.withCredentials = true;
xhr.setRequestHeader("Reflex-Client-Token", getToken());
xhr.setRequestHeader("Reflex-Event-Handler", handler);
// Instrumentation hook (installed by reflex-otel): may add trace headers.
Expand Down
Loading
Loading