Files
ios-client/NetBird
Zoltan Papp b3721c2a71 Fix login flow: wrong server after login, premature auth alert, parallel login (#144)
* Fix login flow: wrong server after login, premature auth alert, parallel login

Three distinct bugs in the iOS login flow:

1. Login ran against (and persisted) the default cloud server instead of the
   user's own management server. performLogin() called NetBirdSDKNewAuth(path, "", nil)
   with an empty management URL. The Go SDK's NewAuth does not read the config file at
   the given path — it builds a fresh in-memory config from the URL argument, and an
   empty URL falls back to DefaultManagementURL (api.netbird.io). Login then ran against
   the wrong server and DirectWriteOutConfig wrote that wrong server back into the
   profile's netbird.cfg. Regression from #138, which moved OAuth into the main-app
   process but passed "" as the URL. Fix: pass the active profile's real management URL
   (ProfileManager.managementURL) and also persist it to the dedicated, logout-surviving
   server URL file on login success.

2. The "Login required" alert popped immediately on Connect even for a first-time
   login. A stale keyLoginRequired flag from a previous extension run was read by the
   polling timer's checkLoginRequiredFlag() right after connect. Fix: clear the flag (and
   any pending auth alert) at the start of performConnect(); a genuinely required re-auth
   is re-signalled by the extension after this connect attempt.

3. The browser login and a parallel VPN start raced. onOpen runs on a background
   goroutine and resumed the continuation before showBrowser was committed on the main
   queue, so loginIfRequired() could observe showBrowser == false and call
   startVPNConnection() — launching the extension, tripping its needsLogin path, and
   popping the auth alert alongside the browser. Fix: set showBrowser and resume the
   continuation in the same main-queue block.

Also persist the chosen server to the dedicated server URL file on iOS when the user
changes the management server in ServerView (previously tvOS-only), so the user's
server cannot fall back to the default cloud.

* Don't signal login-required when user cancels the login browser

Closing the OAuth browser without logging in popped a spurious "Login required"
alert and notification. The browser-finished handler called startVPNConnection()
unconditionally, which launched the extension, tripped its needs-login path, and
signalled login-required — even though the user simply chose not to log in (not a
failed active session).

The ASWebAuthenticationSession callbackURL cannot distinguish success from
cancellation here: NetBird's PKCE flow uses an http://localhost loopback redirect
that the Go HTTP server consumes, so the session completes with a nil callbackURL
even on success. Determine success from the SDK's onLoginSuccess callback instead:

- NetworkExtensionAdapter.loginSucceeded, set by onSuccess (on the main queue) and
  reset at the start of each performLogin().
- SafariView.didFinish starts the VPN only when loginSucceeded; otherwise it calls
  the new ViewModel.cancelPendingLogin(), which resets the Connecting state to
  disconnected and clears the login-required flag and any pending auth alert.

* Abort interactive login when the user dismisses the browser

Closing the OAuth browser without logging in left the SDK's PKCE loopback HTTP
server bound to its redirect port (the iOS login runs in the main-app process and
nothing cancelled it). The next connect then stalled in "Connecting…" while the
new login flow tried to bind the same still-occupied port, eventually failed, and
only later managed to reopen the browser.

cancelPendingLogin() now calls the new NetworkExtensionAdapter.cancelLogin(), which
stops the SDK auth (Auth.Stop, added in the netbird-core submodule). Stopping cancels
the auth context, unblocking WaitToken and shutting down its loopback server so the
port is freed at once. Mirrors Android, where stopEngine() aborts login via the
engine context.

Bumps netbird-core submodule to fix/ios-auth-stop-cancel for Auth.Stop().

NOTE: requires regenerating NetBirdSDK.xcframework on a machine with Xcode
(./build-go-lib.sh) so the Swift binding exposes Auth.stop(); the binding cannot be
built on Linux.
2026-06-29 14:16:52 +02:00
..