-
Notifications
You must be signed in to change notification settings - Fork 0
Recover managed connection authorization #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,9 @@ export const isManagedDevPc = import.meta.env.VITE_DEVPC_MANAGED === "1"; | |||||||||||||||||||
|
|
||||||||||||||||||||
| const BOOTSTRAP_PATH = "/_devpc/bootstrap"; | ||||||||||||||||||||
| const WEBSOCKET_TICKET_PATH = "/_devpc/ws-ticket"; | ||||||||||||||||||||
| const SESSION_RECOVERY_KEY = "devpc-managed-session-recovery-at"; | ||||||||||||||||||||
| const SESSION_RECOVERY_COOLDOWN_MS = 30_000; | ||||||||||||||||||||
| let sessionRecoveryReloadScheduled = false; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| function updateBootstrapMessage(message: string, failed = false): void { | ||||||||||||||||||||
| const root = document.getElementById("root"); | ||||||||||||||||||||
|
|
@@ -50,6 +53,31 @@ function pairingHash(token: string): string { | |||||||||||||||||||
| return new URLSearchParams([["token", token]]).toString(); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| function clearManagedSessionRecovery(): void { | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| window.sessionStorage.removeItem(SESSION_RECOVERY_KEY); | ||||||||||||||||||||
| } catch { | ||||||||||||||||||||
| // Storage can be unavailable in privacy-restricted browser contexts. | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| function scheduleManagedSessionRecovery(): boolean { | ||||||||||||||||||||
| if (sessionRecoveryReloadScheduled) return false; | ||||||||||||||||||||
| const now = Date.now(); | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| const previous = Number(window.sessionStorage.getItem(SESSION_RECOVERY_KEY) ?? 0); | ||||||||||||||||||||
| if (Number.isFinite(previous) && now - previous < SESSION_RECOVERY_COOLDOWN_MS) { | ||||||||||||||||||||
| return false; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| window.sessionStorage.setItem(SESSION_RECOVERY_KEY, String(now)); | ||||||||||||||||||||
| } catch { | ||||||||||||||||||||
| // The in-memory guard still prevents a reload loop within this document. | ||||||||||||||||||||
| } | ||||||||||||||||||||
| sessionRecoveryReloadScheduled = true; | ||||||||||||||||||||
| window.location.reload(); | ||||||||||||||||||||
|
Comment on lines
+73
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In privacy-restricted contexts where Useful? React with 👍 / 👎. |
||||||||||||||||||||
| return true; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| export async function prepareManagedDevPc(): Promise<void> { | ||||||||||||||||||||
| if (!isManagedDevPc) return; | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -104,8 +132,12 @@ export async function prepareManagedWebSocketUrl(socketUrl: string): Promise<str | |||||||||||||||||||
| body: "{}", | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||||
| if ([401, 403].includes(response.status) && scheduleManagedSessionRecovery()) { | ||||||||||||||||||||
| throw new Error("Refreshing the managed workspace connection."); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| throw new Error("The managed workspace connection could not be authorized."); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| clearManagedSessionRecovery(); | ||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Clear recovery state only after the ticket and URL pass validation. Line 140 removes the cooldown for any 2xx response. A malformed credential or invalid WebSocket URL after a 401 then permits the next 401 to trigger another reload. Move this call to immediately before the successful return. Proposed fix- clearManagedSessionRecovery();
const payload = (await response.json()) as {
@@
resolved.searchParams.delete("gatewayTicket");
resolved.searchParams.set(managedSocketUrl ? "wsTicket" : "gatewayTicket", payload.ticket);
+ clearManagedSessionRecovery();
return resolved.toString();📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| const payload = (await response.json()) as { | ||||||||||||||||||||
| ticket?: unknown; | ||||||||||||||||||||
| websocketUrl?: unknown; | ||||||||||||||||||||
|
|
||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: logancsack/t3code
Length of output: 195
🏁 Script executed:
Repository: logancsack/t3code
Length of output: 13313
🏁 Script executed:
Repository: logancsack/t3code
Length of output: 13640
🏁 Script executed:
Repository: logancsack/t3code
Length of output: 205
Test the cooldown and 403 behavior through sessionStorage.
The second 401 call keeps
sessionRecoveryReloadScheduled, soreload()is only exercised through the in-module guard rather than the persisteddevpc-managed-session-recovery-atcooldown. Add an isolated-import case that re-readssessionStoragebefore the secondprepareManagedWebSocketUrl()call, and add the same coverage for a 403 response.🤖 Prompt for AI Agents
Source: Coding guidelines