[client] Clear the login-required latch after installing the new client

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.
This commit is contained in:
Zoltán Papp
2026-07-28 17:41:49 +02:00
parent 408301714e
commit 71df67a2b8

View File

@@ -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)
}