diff --git a/client/ui/frontend/src/lib/connection.ts b/client/ui/frontend/src/lib/connection.ts index b9e98bf24..fca03fc87 100644 --- a/client/ui/frontend/src/lib/connection.ts +++ b/client/ui/frontend/src/lib/connection.ts @@ -51,7 +51,14 @@ async function runSsoLogin( if (uri) await openBrowserLoginUri(uri); const cancelPromise = buildSsoCancelPromise(state, signal); - const waitPromise = Connection.WaitSSOLogin({ userCode: result.userCode, hostname: "" }); + // Combine wait + up in Go so the connection comes up the moment SSO + // completes. During SSO the tray window is hidden and the webview is + // suspended, so a frontend-driven Up (a promise continuation) would not + // fire until the user woke the window (e.g. hovering the tray icon). + const waitPromise = Connection.WaitSSOLoginAndUp( + { userCode: result.userCode, hostname: "" }, + { profileName: "", username: "" }, + ); try { await Promise.race([waitPromise, cancelPromise]); @@ -89,13 +96,13 @@ export async function startConnection(onSettled?: () => void, signal?: AbortSign if (signal?.aborted) state.cancelled = true; if (!state.cancelled && result.needsSsoLogin) { + // runSsoLogin brings the connection up in Go once SSO completes. await runSsoLogin(result, state, signal); - } - - if (!state.cancelled && signal?.aborted) state.cancelled = true; - - if (!state.cancelled) { - await Connection.Up({ profileName: "", username: "" }); + } else { + if (!state.cancelled && signal?.aborted) state.cancelled = true; + if (!state.cancelled) { + await Connection.Up({ profileName: "", username: "" }); + } } } catch (e) { WindowManager.CloseBrowserLogin().catch(console.error); diff --git a/client/ui/services/connection.go b/client/ui/services/connection.go index 8e7919af6..36f64173b 100644 --- a/client/ui/services/connection.go +++ b/client/ui/services/connection.go @@ -162,6 +162,27 @@ func (s *Connection) Up(ctx context.Context, p UpParams) error { return nil } +// WaitSSOLoginAndUp blocks until the SSO login completes and then brings the +// connection up, both from the Go side. Keeping the post-login Up here rather +// than as a frontend continuation is deliberate: during SSO the tray window is +// hidden and the webview is suspended (macOS App Nap / hidden-window timer +// throttling), so a frontend-driven Up would not run until the user woke the +// window (e.g. by hovering the tray icon). Doing it in Go connects the moment +// the daemon reports SSO success. Returns the authenticated user's email. +func (s *Connection) WaitSSOLoginAndUp(ctx context.Context, wait WaitSSOParams, up UpParams) (string, error) { + email, err := s.WaitSSOLogin(ctx, wait) + if err != nil { + return "", err + } + if err := ctx.Err(); err != nil { + return "", err + } + if err := s.Up(ctx, up); err != nil { + return "", err + } + return email, nil +} + func (s *Connection) Down(ctx context.Context) error { cli, err := s.conn.Client() if err != nil {