From 71df67a2b8b76d003713dfcf1b8bdb01b804dd4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Papp?= Date: Tue, 28 Jul 2026 17:41:49 +0200 Subject: [PATCH] [client] Clear the login-required latch after installing the new client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run() released the latch before setState() swapped in the fresh connect client, so Status() calls landing in that window still read the previous run's context state — which holds the NeedsLogin that prompted the login — and re-latched what had just been cleared. The generation guard does not catch this: the clear precedes the observation, so the generation matches and the store counts as current. The state-change goroutine calls Status() on every recorder tick, and the login path emits several, so the window is ordinary traffic rather than a rare interleaving. Clear once the replacement client is installed instead. --- client/android/client.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/android/client.go b/client/android/client.go index 18c414e67..7249cf6f7 100644 --- a/client/android/client.go +++ b/client/android/client.go @@ -166,14 +166,16 @@ func (c *Client) Run(platformFiles PlatformFiles, urlOpener URLOpener, isAndroid if err != nil { return err } - // This path runs the interactive SSO flow, so reaching here means the peer - // is authenticated again — release the latch Status() reports from. - c.clearLoginRequired() - // todo do not throw error in case of cancelled context ctx = internal.CtxInitState(ctx) connectClient := internal.NewConnectClient(ctx, cfg, c.recorder) c.setState(cfg, cacheDir, connectClient) + // This path runs the interactive SSO flow, so reaching here means the peer + // is authenticated again — release the latch Status() reports from. Clear + // only once the fresh connect client is installed: until then Status() + // still reads the previous run's context state, which holds the NeedsLogin + // that prompted this login, and would re-latch what was just cleared. + c.clearLoginRequired() return connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, slices.Clone(dns.items), dnsReadyListener, stateFile, cacheDir) }