From 98b760ce6468e60b64ab6806ba404de747ef220e Mon Sep 17 00:00:00 2001 From: LukeGus Date: Mon, 26 Jan 2026 00:28:40 -0600 Subject: [PATCH] fix: add host issue, screen flickering issue, 2fa issue, open terminal session icon missing issue --- src/backend/database/routes/ssh.ts | 175 +++++++------------------ src/backend/database/routes/users.ts | 4 +- src/backend/ssh/terminal.ts | 5 +- src/backend/utils/auth-manager.ts | 9 ++ src/ui/desktop/DesktopApp.tsx | 4 - src/ui/desktop/authentication/Auth.tsx | 4 +- src/ui/main-axios.ts | 26 +++- 7 files changed, 88 insertions(+), 139 deletions(-) diff --git a/src/backend/database/routes/ssh.ts b/src/backend/database/routes/ssh.ts index d7c2826c..dad37113 100644 --- a/src/backend/database/routes/ssh.ts +++ b/src/backend/database/routes/ssh.ts @@ -49,6 +49,50 @@ function isValidPort(port: unknown): port is number { return typeof port === "number" && port > 0 && port <= 65535; } +function transformHostResponse( + host: Record, +): Record { + return { + ...host, + tags: + typeof host.tags === "string" + ? host.tags + ? host.tags.split(",").filter(Boolean) + : [] + : [], + pin: !!host.pin, + enableTerminal: !!host.enableTerminal, + enableTunnel: !!host.enableTunnel, + enableFileManager: !!host.enableFileManager, + enableDocker: !!host.enableDocker, + showTerminalInSidebar: !!host.showTerminalInSidebar, + showFileManagerInSidebar: !!host.showFileManagerInSidebar, + showTunnelInSidebar: !!host.showTunnelInSidebar, + showDockerInSidebar: !!host.showDockerInSidebar, + showServerStatsInSidebar: !!host.showServerStatsInSidebar, + tunnelConnections: host.tunnelConnections + ? JSON.parse(host.tunnelConnections as string) + : [], + jumpHosts: host.jumpHosts ? JSON.parse(host.jumpHosts as string) : [], + quickActions: host.quickActions + ? JSON.parse(host.quickActions as string) + : [], + statsConfig: host.statsConfig + ? JSON.parse(host.statsConfig as string) + : undefined, + terminalConfig: host.terminalConfig + ? JSON.parse(host.terminalConfig as string) + : undefined, + dockerConfig: host.dockerConfig + ? JSON.parse(host.dockerConfig as string) + : undefined, + forceKeyboardInteractive: host.forceKeyboardInteractive === "true", + socks5ProxyChain: host.socks5ProxyChain + ? JSON.parse(host.socks5ProxyChain as string) + : [], + }; +} + const authManager = AuthManager.getInstance(); const permissionManager = PermissionManager.getInstance(); const authenticateJWT = authManager.createAuthMiddleware(); @@ -461,34 +505,7 @@ router.post( } const createdHost = result; - const baseHost = { - ...createdHost, - tags: - typeof createdHost.tags === "string" - ? createdHost.tags - ? createdHost.tags.split(",").filter(Boolean) - : [] - : [], - pin: !!createdHost.pin, - enableTerminal: !!createdHost.enableTerminal, - enableTunnel: !!createdHost.enableTunnel, - tunnelConnections: createdHost.tunnelConnections - ? JSON.parse(createdHost.tunnelConnections as string) - : [], - jumpHosts: createdHost.jumpHosts - ? JSON.parse(createdHost.jumpHosts as string) - : [], - enableFileManager: !!createdHost.enableFileManager, - enableDocker: !!createdHost.enableDocker, - showTerminalInSidebar: !!createdHost.showTerminalInSidebar, - showFileManagerInSidebar: !!createdHost.showFileManagerInSidebar, - showTunnelInSidebar: !!createdHost.showTunnelInSidebar, - showDockerInSidebar: !!createdHost.showDockerInSidebar, - showServerStatsInSidebar: !!createdHost.showServerStatsInSidebar, - statsConfig: createdHost.statsConfig - ? JSON.parse(createdHost.statsConfig as string) - : undefined, - }; + const baseHost = transformHostResponse(createdHost); const resolvedHost = (await resolveHostCredentials(baseHost, userId)) || baseHost; @@ -1067,37 +1084,7 @@ router.put( } const updatedHost = updatedHosts[0]; - const baseHost = { - ...updatedHost, - tags: - typeof updatedHost.tags === "string" - ? updatedHost.tags - ? updatedHost.tags.split(",").filter(Boolean) - : [] - : [], - pin: !!updatedHost.pin, - enableTerminal: !!updatedHost.enableTerminal, - enableTunnel: !!updatedHost.enableTunnel, - tunnelConnections: updatedHost.tunnelConnections - ? JSON.parse(updatedHost.tunnelConnections as string) - : [], - jumpHosts: updatedHost.jumpHosts - ? JSON.parse(updatedHost.jumpHosts as string) - : [], - enableFileManager: !!updatedHost.enableFileManager, - enableDocker: !!updatedHost.enableDocker, - showTerminalInSidebar: !!updatedHost.showTerminalInSidebar, - showFileManagerInSidebar: !!updatedHost.showFileManagerInSidebar, - showTunnelInSidebar: !!updatedHost.showTunnelInSidebar, - showDockerInSidebar: !!updatedHost.showDockerInSidebar, - showServerStatsInSidebar: !!updatedHost.showServerStatsInSidebar, - statsConfig: updatedHost.statsConfig - ? JSON.parse(updatedHost.statsConfig as string) - : undefined, - dockerConfig: updatedHost.dockerConfig - ? JSON.parse(updatedHost.dockerConfig as string) - : undefined, - }; + const baseHost = transformHostResponse(updatedHost); const resolvedHost = (await resolveHostCredentials(baseHost, userId)) || baseHost; @@ -1302,41 +1289,7 @@ router.get( const result = await Promise.all( data.map(async (row: Record) => { const baseHost = { - ...row, - tags: - typeof row.tags === "string" - ? row.tags - ? row.tags.split(",").filter(Boolean) - : [] - : [], - pin: !!row.pin, - enableTerminal: !!row.enableTerminal, - enableTunnel: !!row.enableTunnel, - tunnelConnections: row.tunnelConnections - ? JSON.parse(row.tunnelConnections as string) - : [], - jumpHosts: row.jumpHosts ? JSON.parse(row.jumpHosts as string) : [], - quickActions: row.quickActions - ? JSON.parse(row.quickActions as string) - : [], - enableFileManager: !!row.enableFileManager, - enableDocker: !!row.enableDocker, - showTerminalInSidebar: !!row.showTerminalInSidebar, - showFileManagerInSidebar: !!row.showFileManagerInSidebar, - showTunnelInSidebar: !!row.showTunnelInSidebar, - showDockerInSidebar: !!row.showDockerInSidebar, - showServerStatsInSidebar: !!row.showServerStatsInSidebar, - statsConfig: row.statsConfig - ? JSON.parse(row.statsConfig as string) - : undefined, - terminalConfig: row.terminalConfig - ? JSON.parse(row.terminalConfig as string) - : undefined, - forceKeyboardInteractive: row.forceKeyboardInteractive === "true", - socks5ProxyChain: row.socks5ProxyChain - ? JSON.parse(row.socks5ProxyChain as string) - : [], - + ...transformHostResponse(row), isShared: !!row.isShared, permissionLevel: row.permissionLevel || undefined, sharedExpiresAt: row.expiresAt || undefined, @@ -1417,39 +1370,7 @@ router.get( } const host = data[0]; - const result = { - ...host, - tags: - typeof host.tags === "string" - ? host.tags - ? host.tags.split(",").filter(Boolean) - : [] - : [], - pin: !!host.pin, - enableTerminal: !!host.enableTerminal, - enableTunnel: !!host.enableTunnel, - tunnelConnections: host.tunnelConnections - ? JSON.parse(host.tunnelConnections) - : [], - jumpHosts: host.jumpHosts ? JSON.parse(host.jumpHosts) : [], - quickActions: host.quickActions ? JSON.parse(host.quickActions) : [], - enableFileManager: !!host.enableFileManager, - showTerminalInSidebar: !!host.showTerminalInSidebar, - showFileManagerInSidebar: !!host.showFileManagerInSidebar, - showTunnelInSidebar: !!host.showTunnelInSidebar, - showDockerInSidebar: !!host.showDockerInSidebar, - showServerStatsInSidebar: !!host.showServerStatsInSidebar, - statsConfig: host.statsConfig - ? JSON.parse(host.statsConfig) - : undefined, - terminalConfig: host.terminalConfig - ? JSON.parse(host.terminalConfig) - : undefined, - forceKeyboardInteractive: host.forceKeyboardInteractive === "true", - socks5ProxyChain: host.socks5ProxyChain - ? JSON.parse(host.socks5ProxyChain) - : [], - }; + const result = transformHostResponse(host); res.json((await resolveHostCredentials(result, userId)) || result); } catch (err) { diff --git a/src/backend/database/routes/users.ts b/src/backend/database/routes/users.ts index 9d1e3bf3..cb51fa05 100644 --- a/src/backend/database/routes/users.ts +++ b/src/backend/database/routes/users.ts @@ -1145,7 +1145,7 @@ router.get("/oidc/callback", async (req, res) => { ? 30 * 24 * 60 * 60 * 1000 : 7 * 24 * 60 * 60 * 1000; - res.clearCookie("jwt", authManager.getSecureCookieOptions(req)); + res.clearCookie("jwt", authManager.getClearCookieOptions(req)); return res .cookie("jwt", token, authManager.getSecureCookieOptions(req, maxAge)) @@ -1439,7 +1439,7 @@ router.post("/logout", authenticateJWT, async (req, res) => { } return res - .clearCookie("jwt", authManager.getSecureCookieOptions(req)) + .clearCookie("jwt", authManager.getClearCookieOptions(req)) .json({ success: true, message: "Logged out successfully" }); } catch (err) { authLogger.error("Logout failed", err); diff --git a/src/backend/ssh/terminal.ts b/src/backend/ssh/terminal.ts index b8f8ba94..37bc2b88 100644 --- a/src/backend/ssh/terminal.ts +++ b/src/backend/ssh/terminal.ts @@ -1284,7 +1284,10 @@ wss.on("connection", async (ws: WebSocket, req) => { return; } - connectConfig.password = resolvedCredentials.password; + if (!hostConfig.forceKeyboardInteractive) { + connectConfig.password = resolvedCredentials.password; + } + sendLog("auth", "info", "Using password authentication"); } else if ( resolvedCredentials.authType === "key" && diff --git a/src/backend/utils/auth-manager.ts b/src/backend/utils/auth-manager.ts index 10dd5662..277c5731 100644 --- a/src/backend/utils/auth-manager.ts +++ b/src/backend/utils/auth-manager.ts @@ -518,6 +518,15 @@ class AuthManager { }; } + getClearCookieOptions(req: RequestWithHeaders) { + return { + httpOnly: false, + secure: req.secure || req.headers["x-forwarded-proto"] === "https", + sameSite: "strict" as const, + path: "/", + }; + } + createAuthMiddleware() { return async (req: Request, res: Response, next: NextFunction) => { const authReq = req as AuthenticatedRequest; diff --git a/src/ui/desktop/DesktopApp.tsx b/src/ui/desktop/DesktopApp.tsx index 0a677bf6..b068dcbb 100644 --- a/src/ui/desktop/DesktopApp.tsx +++ b/src/ui/desktop/DesktopApp.tsx @@ -255,10 +255,6 @@ function AppContent({ setTimeout(async () => { try { await logoutUser(); - - if (isElectron()) { - localStorage.removeItem("jwt"); - } } catch (error) { console.error("Logout failed:", error); } diff --git a/src/ui/desktop/authentication/Auth.tsx b/src/ui/desktop/authentication/Auth.tsx index b303d177..1001479c 100644 --- a/src/ui/desktop/authentication/Auth.tsx +++ b/src/ui/desktop/authentication/Auth.tsx @@ -309,7 +309,7 @@ export function Auth({ "*", ); setWebviewAuthSuccess(true); - setTimeout(() => window.location.reload(), 100); + return; } catch (e) { console.error("Error posting auth success message:", e); } @@ -518,7 +518,6 @@ export function Auth({ "*", ); setWebviewAuthSuccess(true); - setTimeout(() => window.location.reload(), 100); setTotpLoading(false); return; } catch (e) { @@ -655,7 +654,6 @@ export function Auth({ "*", ); setWebviewAuthSuccess(true); - setTimeout(() => window.location.reload(), 100); setOidcLoading(false); return; } catch (e) { diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts index cb041f3f..ecfdf359 100644 --- a/src/ui/main-axios.ts +++ b/src/ui/main-axios.ts @@ -1029,7 +1029,7 @@ export async function createSSHHost(hostData: SSHHostData): Promise { return response.data; } } catch (error) { - handleApiError(error, "create SSH host"); + throw handleApiError(error, "create SSH host"); } } @@ -1107,7 +1107,7 @@ export async function updateSSHHost( return response.data; } } catch (error) { - handleApiError(error, "update SSH host"); + throw handleApiError(error, "update SSH host"); } } @@ -2381,8 +2381,30 @@ export async function logoutUser(): Promise<{ }> { try { const response = await authApi.post("/users/logout"); + + if (isElectron()) { + localStorage.removeItem("jwt"); + electronSettingsCache.delete("jwt"); + } else { + const isSecure = window.location.protocol === "https:"; + const cookieString = isSecure + ? "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; Secure; SameSite=Strict" + : "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Strict"; + document.cookie = cookieString; + } + return response.data; } catch (error) { + if (isElectron()) { + localStorage.removeItem("jwt"); + electronSettingsCache.delete("jwt"); + } else { + const isSecure = window.location.protocol === "https:"; + const cookieString = isSecure + ? "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; Secure; SameSite=Strict" + : "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Strict"; + document.cookie = cookieString; + } handleApiError(error, "logout user"); } }