Case study 1 of 2 · all case studies
Silent SSO session expiry: when “Connected” stops being true
A worked example of turning one public bug report (netbirdio/netbird#5513 (opens in a new tab)) into a product decision, requirements, and a UX concept.
What the issue says
Live metadata
- State
- Open
- Opened
- 5 Mar 2026
- Last updated
- 12 Oct 2026
- Comments
- 3
- Labels
- triage-needed
- Data
- Live from GitHub (cached up to 15 minutes)
The user’s expectation
The reporter expects one of three things when the sign-in lapses: the client renews it automatically, it starts a new login by itself, or at minimum its status says that a login is needed instead of staying on “Connected”.
The reporter’s only workaround is a manual disconnect and reconnect, which does not suit unattended servers or CI runners.
Evidence and verification status
A product decision is only as good as its inputs. Each claim is labelled with who said it and whether anyone has verified it.
- ReportedIssue description by the reporter
After the SSO session expires, the client still shows Management and Signal as Connected, but some peers stay on “Connecting” and traffic to them stops. No re-authentication prompt appears.
- ReportedIssue description by the reporter
Running the client down and up again triggers a new SSO login and restores all peers.
- ReportedIssue description by the reporter
Self-hosted control plane with Azure AD (Entra ID) over OpenID Connect, client version 0.66.2.
- Reported by othersTwo later comments from other users, with no logs or versions
The same symptom with other identity providers (Keycloak, PocketID), on several clients.
- Maintainer statementMaintainer comment, 12 Oct 2026
Version 0.66.2 predates reworked session-expiry handling (0.75.0) and a relogin fix (0.76.2). The maintainer asked for a retest on 0.76.2 or later, plus diagnostics.
- UnknownNobody has reported this yet
The problem still occurs on 0.76.2 or later.
Step 0: validate before building
The newest comment changes the right first move. A maintainer says the reported version predates relevant fixes, so the first product decision is about evidence, not features.
Retest on 0.76.2 or later. Use the reporter's reproduction steps. This single result decides how much of the rest of this page is needed.
Collect the diagnostics the maintainer asked for. Operating system and architecture, the client version, detailed status output, and a short debug capture taken while the problem is happening. Read it before sharing: anonymizing is not full redaction.
Check the dashboard. Does it show the peer's login as expired, and was the peer enrolled by SSO or by a setup key?
If it no longer reproduces
Confirm with the reporter and close the bug. Keep R5 to R8 as a smaller backlog item about observability: can an operator tell, in seconds, that a client needs a new sign-in?
If it still reproduces
Use the diagnostics to choose between the hypotheses below, then schedule R1 to R4 as one release-scoped epic.
Problem framing and hypotheses
Three separable problems are bundled in the report. Separating them lets the team ship the cheapest honest improvement first.
Session lifecycle
Does the client renew or replace an expiring sign-in on its own?
State honesty
Does the status shown to people and scripts match reality?
Recovery
How quickly, and with how little effort, does a user or an unattended machine get back to healthy?
Hypotheses (unverified, from the outside)
H1Status reflects the control-plane connection, not the sign-in state
How to test: After forcing a session to expire, compare the client's overall status, the per-peer states, and the dashboard's view of the same peer.
H2Silent token refresh fails or is unavailable, and the failure is not surfaced
How to test: Check whether the identity provider issues refresh tokens for this client, how long they live, and what the client logs when a refresh fails.
H3The server side expires the peer's login while the client keeps its connections open
How to test: Reproduce with an SSO-enrolled peer and with a setup-key peer on the same version. The maintainer notes that peer expiry only affects SSO-enrolled peers, so the setup-key peer is the control.
Proposed requirements
Written as user-visible outcomes, not implementation. Priorities use MoSCoW and assume the problem is confirmed in Step 0.
- R1Must
When the sign-in is expired or invalid, the overall client status must not read as plain “Connected”. It shows a distinct “Sign-in required” state.
Why: Removes the silent failure at the heart of the report (H1).
Verified by: Expire a session in a test environment; assert the status output and the UI headline both change within the agreed time bound.
- R2Must
Peers that cannot be reached because of an expired sign-in show a specific reason, not a generic “Connecting”.
Why: Points users at the cause instead of sending them to debug networking.
Verified by: Peer list shows “Waiting for sign-in” for exactly the affected peers; unaffected peers keep their normal state.
- R3Must
Where the identity provider supports it, the client renews the session silently before it expires, and shows a transient “Refreshing” state if renewal takes noticeable time.
Why: Best outcome: nobody notices the expiry (H2).
Verified by: Run across a full token lifetime with refresh enabled; no manual action and no loss of peers.
- R4Should
When silent renewal is impossible, interactive clients show a clear prompt with one action; the CLI prints the next command to run and exits non-zero from a status check.
Why: Gives a short path from detection to recovery.
Verified by: Prompt appears once per expiry, not repeatedly; the action completes the login flow and restores peers.
- R5Should
Status is available in a machine-readable form (a session field in JSON output) so monitoring can alert on unattended machines.
Why: The report mentions servers and CI runners where nobody watches the screen.
Verified by: A script can distinguish healthy, refreshing, and sign-in-required states without parsing prose.
- R6Should
Documentation explains which enrolment method suits unattended machines and what peer expiry does and does not affect.
Why: May resolve part of the problem with no code change.
Verified by: A reader can choose between SSO and setup-key enrolment for a server, from one page.
- R7Could
The management dashboard shows “Login expired” per peer and can warn administrators before many sessions lapse at once.
Why: Gives administrators a fleet-level view (H3).
Verified by: Dashboard state matches the client state for the same peer.
- R8Must
Debug bundles record when the session was issued, refreshed, and expired, without ever recording token values.
Why: Makes the next report like this one diagnosable in a single round trip.
Verified by: Bundle contains timestamps and states; automated check proves no token strings are present.
Non-goals
- Redesigning authentication or changing identity-provider policies, which customers own.
- Forcing interactive prompts on headless machines. They need silent renewal or a different enrolment method.
- Shipping anything from this prototype into NetBird. It is a portfolio concept only.
Mock connection-state UX
The same machine, five moments. State 1 is the behavior in the report; states 2 to 5 are the proposal. Step through them to see what changes for a person and for a script.
Connected
Connected
Nothing on screen says the sign-in has expired.
Peers (2 of 5 connected)
- build-runner-01Connected
- db-serverConnecting
- laptop-anaConnecting
- nas-homeConnected
- ci-runner-02Connecting
$ netbird status
Management: Connected
Signal: Connected
Peers count: 2/5 ConnectedWording and field names are invented for this concept. They are not NetBird’s real output.
Design rationale
- This is the problem described in the issue: the headline is green while three peers are stuck on “Connecting”.
- A user or script that trusts the headline has no reason to run a manual disconnect and reconnect.
- The state is drawn from the issue text. It is not a recording of any NetBird version.
State is never conveyed by colour alone: every status pairs an icon with text, and changes are announced to screen readers.
Success metrics, risks, open questions
Proposed metrics
No baselines exist yet; each needs one before it can be a target.
- Time from session expiry to the status changing.
- Time from expiry to restored connectivity.
- Support or issue reports mentioning a stale “Connected” state.
Risks and trade-offs
- Scripts may parse today’s status text; changing it needs a compatibility plan.
- Prompting too eagerly annoys users; too late hides the problem.
- Logging session details must never include tokens.
Open questions
- Does it reproduce on 0.76.2 or later?
- Which identity providers give the client a usable expiry time and refresh token?
- How many affected machines are SSO-enrolled versus setup-key?
Ticket draft from the Lab
The triage board produced this unedited, from the issue text and its suggested scores, using the fixed template. It shows the template's generic starting point; the sections above are what a human adds on top.
Open this issue on the triage board to adjust its scores and watch the draft update.
Ticket title
Harden: SSO token expiry leaves client in stale "Connected" state — requires manual disconnect/reconnect to re-authenticate
Problem statement
Reported in netbirdio/netbird#5513 on 5 Mar 2026: "SSO token expiry leaves client in stale "Connected" state — requires manual disconnect/reconnect to re-authenticate".
Reporter's description (excerpt, unverified): When the SSO session (Azure AD OpenID Connect) expires, the NetBird client remains in a "Connected" state (Management: Connected, Signal: Connected) but peer connectivity silently degrades. Some peers show as "Connecting" indefinitely and traffic no longer flows to them, while the client never triggers a…
Draft estimate, not verified: impact 3/5, urgency 5/5, effort 3/5, giving a priority score of 5 (Medium).
GitHub labels: triage-needed. Category: Reliability.
User story
As an operator running NetBird on long-lived machines, I want the failure described in "SSO token expiry leaves client in stale "Connected" state — requires manual disconnect/reconnect to re-authenticate" to be detected and recovered from (or clearly surfaced), so that connectivity does not silently degrade.
Acceptance criteria
- Given the failure condition from the report occurs, when the system is affected, then the problem is detected within a defined time bound (to be agreed) instead of failing silently.
- Given a detected failure, when automatic recovery is possible, then the system recovers without manual intervention; otherwise it surfaces an actionable status.
- Given the system is degraded, when a user or script checks status, then the status reflects the real state rather than reporting healthy.
- Diagnostics (logs or metrics) record when the failure was detected and how it was resolved, without leaking secrets.
QA test scenarios
Confirm the report still reproduces
- Identify the affected component and the version in the report.
- Retest on the latest supported release, using the reporter's reproduction steps.
Expected: Either the behavior is reproduced (proceed) or it is not (ask the reporter to confirm, then close or re-scope).
Induce the failure and observe detection
- Create the failure condition from the report (for example expire a session or cut the network).
- Wait for the agreed detection window.
Expected: The failure is detected and status changes accordingly; nothing keeps reporting healthy.
Recovery without manual action
- Restore the failed dependency.
- Observe the system without intervening.
Expected: The system returns to a healthy state on its own, or shows exactly what action is required.
Long-running soak
- Run the affected component unattended for an extended period with periodic fault injection.
Expected: No resource growth, hangs, or silent degradation.
Assumptions and open questions
- The problem has not been reproduced by this tool; it is taken from a single public report.
- The reporter's version and environment may be outdated; confirm against the latest release first.
- Impact and urgency are suggested defaults or manual edits, not measurements. Effort (3/5) needs an engineering estimate.
- The persona in the user story is a template default; adjust it to the real affected audience.
Next: Case study 2: a light-theme request that stayed open for two years, a feature request with an accessibility angle and a very different shape of evidence.