mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-12 17:59:06 +02:00
[client] Show the SSO login popup and open the browser from Go
The browser-login popup was created hidden and relied on its own webview to size and show itself and to launch the external browser. On macOS a hidden WKWebView gets throttled or suspended (App Nap / hidden-window throttling), so on the first-use path nothing appeared and the browser never opened, leaving the session-expiration dialog disabled until the PKCE flow timed out. Reproduced by freezing the popup's WebContent process: the old code showed nothing, the new code shows the popup and opens the browser within 30 ms regardless of the webview state. Show and focus the popup from Go right after creation and launch the browser from Go on both the create and reuse paths. The popup's frontend no longer shows or focuses itself, so the browser keeps the foreground once it activates. This also fixes the reuse path, where a fragment-only SetURL kept the mounted React tree and the once-only guard skipped opening the browser for the new URI. Browser launch failures surface in the error dialog instead of being swallowed.
This commit is contained in:
@@ -5,7 +5,11 @@ import { isLinux } from "@/lib/platform";
|
||||
|
||||
// Sizes the current Wails window to the measured content height (keeping `width`),
|
||||
// then shows it. Re-applies on content resize and language change.
|
||||
export function useAutoSizeWindow<T extends HTMLElement>(width: number, ready: boolean = true) {
|
||||
export function useAutoSizeWindow<T extends HTMLElement>(
|
||||
width: number,
|
||||
ready: boolean = true,
|
||||
showWhenSized: boolean = true,
|
||||
) {
|
||||
const ref = useRef<T | null>(null);
|
||||
useLayoutEffect(() => {
|
||||
const el = ref.current;
|
||||
@@ -33,7 +37,7 @@ export function useAutoSizeWindow<T extends HTMLElement>(width: number, ready: b
|
||||
await Window.SetMaxSize(width, targetH);
|
||||
}
|
||||
await Window.SetSize(width, targetH);
|
||||
showOnce();
|
||||
if (showWhenSized) showOnce();
|
||||
} catch {
|
||||
// window gone / not ready — ignore
|
||||
}
|
||||
@@ -55,6 +59,6 @@ export function useAutoSizeWindow<T extends HTMLElement>(width: number, ready: b
|
||||
cancelAnimationFrame(raf2);
|
||||
i18next.off("languageChanged", scheduleApply);
|
||||
};
|
||||
}, [width, ready]);
|
||||
}, [width, ready, showWhenSized]);
|
||||
return ref;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { Events } from "@wailsio/runtime";
|
||||
@@ -20,8 +20,7 @@ export default function LoginWaitingForBrowserDialog() {
|
||||
const { t } = useTranslation();
|
||||
const [params] = useSearchParams();
|
||||
const uri = params.get("uri") ?? "";
|
||||
const contentRef = useAutoSizeWindow<HTMLDivElement>(WINDOW_WIDTH);
|
||||
const openedRef = useRef(false);
|
||||
const contentRef = useAutoSizeWindow<HTMLDivElement>(WINDOW_WIDTH, true, false);
|
||||
|
||||
const reportOpenFailure = useCallback(
|
||||
(e: unknown) => {
|
||||
@@ -33,13 +32,6 @@ export default function LoginWaitingForBrowserDialog() {
|
||||
[t],
|
||||
);
|
||||
|
||||
// Open the browser only after mount, or it lands on top of the still-hidden popup.
|
||||
useEffect(() => {
|
||||
if (!uri || openedRef.current) return;
|
||||
openedRef.current = true;
|
||||
Connection.OpenURL(uri).catch(reportOpenFailure);
|
||||
}, [uri, reportOpenFailure]);
|
||||
|
||||
const tryAgain = useCallback(() => {
|
||||
if (!uri) return;
|
||||
Connection.OpenURL(uri).catch(reportOpenFailure);
|
||||
|
||||
@@ -205,19 +205,7 @@ func (s *Connection) Down(ctx context.Context) error {
|
||||
// window.open, so the SSO verification page can't pop inline. Honors $BROWSER
|
||||
// before the platform default.
|
||||
func (s *Connection) OpenURL(url string) error {
|
||||
if browser := os.Getenv("BROWSER"); browser != "" {
|
||||
return exec.Command(browser, url).Start()
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
case "darwin":
|
||||
return exec.Command("open", url).Start()
|
||||
case "linux":
|
||||
return exec.Command("xdg-open", url).Start()
|
||||
default:
|
||||
return fmt.Errorf("unsupported platform")
|
||||
}
|
||||
return openURL(url)
|
||||
}
|
||||
|
||||
func (s *Connection) Logout(ctx context.Context, p LogoutParams) error {
|
||||
@@ -288,3 +276,19 @@ func (s *Connection) waitSSOLogin(ctx context.Context, p WaitSSOParams) (string,
|
||||
func (s *Connection) classifyDaemonError(err error) *ClientError {
|
||||
return s.classifier.classify(err)
|
||||
}
|
||||
|
||||
func openURL(url string) error {
|
||||
if browser := os.Getenv("BROWSER"); browser != "" {
|
||||
return exec.Command(browser, url).Start()
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
case "darwin":
|
||||
return exec.Command("open", url).Start()
|
||||
case "linux":
|
||||
return exec.Command("xdg-open", url).Start()
|
||||
default:
|
||||
return fmt.Errorf("unsupported platform")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,11 @@ func (s *WindowManager) OpenBrowserLogin(uri string) {
|
||||
s.app.Event.Emit(EventBrowserLoginCancel)
|
||||
}
|
||||
})
|
||||
s.centerOnCursorScreen(s.browserLogin)
|
||||
s.centerOnCursorScreen(bl)
|
||||
bl.Show()
|
||||
bl.Focus()
|
||||
log.Debugf("browser-login popup created and shown")
|
||||
s.openBrowser(uri)
|
||||
return
|
||||
}
|
||||
if uri != "" {
|
||||
@@ -254,6 +258,20 @@ func (s *WindowManager) OpenBrowserLogin(uri string) {
|
||||
s.centerOnCursorScreen(s.browserLogin)
|
||||
s.browserLogin.Show()
|
||||
s.browserLogin.Focus()
|
||||
log.Debugf("browser-login popup reused")
|
||||
s.openBrowser(uri)
|
||||
}
|
||||
|
||||
func (s *WindowManager) openBrowser(uri string) {
|
||||
if uri == "" {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
if err := openURL(uri); err != nil {
|
||||
log.Errorf("open browser for SSO login: %v", err)
|
||||
s.OpenError(s.title("browserLogin.openFailedTitle"), err.Error(), "")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// BrowserLoginWindow returns the live SSO popup, or nil. While non-nil it is the
|
||||
|
||||
Reference in New Issue
Block a user