From 166c6118e24e71db2b12a7792239df745840e5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Papp?= Date: Wed, 3 Jun 2026 17:19:52 +0200 Subject: [PATCH] [client] Fix Up failing with NeedsLogin after SSO login After a successful WaitSSOLogin the daemon deliberately stays in StatusNeedsLogin, and after a mid-session expiry (peer kicked out by the management server) the engine tears down with clientRunning == false. In both cases the caller's Up takes the fresh-start branch, which only accepted StatusIdle and rejected NeedsLogin with "up already in progress: current status NeedsLogin". This forced a second Up to actually connect (CLI: re-run `netbird up`; GUI: click Connect again). Treat NeedsLogin as a legitimate fresh-start entry state and reset it to Idle before starting the engine, so the first Up after login drives Connecting -> Connected directly. --- client/server/server.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/client/server/server.go b/client/server/server.go index 6b070b1e7..17107b8f2 100644 --- a/client/server/server.go +++ b/client/server/server.go @@ -793,6 +793,22 @@ func (s *Server) Up(callerCtx context.Context, msg *proto.UpRequest) (*proto.UpR return nil, err } + // StatusNeedsLogin is a legitimate fresh-start entry state: a successful + // WaitSSOLogin deliberately leaves the daemon in NeedsLogin (the login is + // done, the token is in hand, but the engine hasn't been brought up yet — + // see WaitSSOLogin's state-transition table). The same holds after a + // mid-session expiry tore the engine down (clientRunning == false) and the + // user re-authenticated. In both cases the caller's Up is expected to drive + // the connection; treat NeedsLogin like Idle and reset to Idle so the + // engine's own StatusConnecting → StatusConnected progression starts from a + // clean slate. Without this, the first Up after an SSO login fails with + // "up already in progress" and the user has to trigger Up a second time + // (CLI: re-run `netbird up`; GUI: click Connect again). + if status == internal.StatusNeedsLogin { + status = internal.StatusIdle + state.Set(internal.StatusIdle) + } + if status != internal.StatusIdle { s.mutex.Unlock() return nil, fmt.Errorf("up already in progress: current status %s", status)