Skip to content

Settings Security reports authentication as disabled when status loading fails #5479

Description

@atomantic

Problem

Settings > Security collapses “authentication status could not be loaded” into “authentication is disabled.” SecurityTab initializes enabled to false at client/src/components/settings/SecurityTab.jsx:15, suppresses every getAuthStatus rejection at client/src/components/settings/SecurityTab.jsx:24, and still clears the loading state at client/src/components/settings/SecurityTab.jsx:28. The settled UI then renders Login password disabled at client/src/components/settings/SecurityTab.jsx:93 and selects the first-time Set a password / Enable login branch at client/src/components/settings/SecurityTab.jsx:112 and client/src/components/settings/SecurityTab.jsx:148.

That is not a valid fallback for this endpoint. /api/auth/status is the authoritative public status read (client/src/services/apiAuth.js:5, server/routes/auth.js:37); a rejected or malformed response says nothing about whether auth is enabled.

Trigger

  1. Open /settings/security.
  2. Have GET /api/auth/status reject because the server is temporarily unreachable or its settings read fails.
  3. The request is silent and the component's catch returns null, so the spinner disappears and the page reports that the login password is disabled.
  4. If auth is actually enabled, the page also omits the current-password field and presents the wrong mutation flow; the later password request can only fail after the user has acted on the false status.

Impact

The security screen gives a definitive, incorrect answer about whether the instance is password-gated. Users can make access-control decisions from stale fiction and are led into a form that cannot succeed for the actual server state.

Fix

  • In client/src/components/settings/SecurityTab.jsx, represent status as an explicit sentinel (null = not loaded) plus a load error; do not initialize a real disabled state before a validated response arrives.
  • Accept only a response containing a boolean enabled. Treat a rejected or malformed response as an error rather than coercing it with !!s?.enabled.
  • While status is unknown, render neither the enabled nor disabled password forms. Show an inline error with a Retry button that re-runs the status request and restores the appropriate form only after a valid response.
  • Add client/src/components/settings/SecurityTab.test.jsx covering enabled and disabled success responses, rejection, malformed success payload, and successful retry.

Rejected alternative: toast the load error and continue rendering the disabled branch. A transient toast does not repair the false persistent state, and the wrong mutation form would remain actionable.

Dispatch rationale: model:light fits a localized state/render correction; effort:medium is warranted because the unknown/error/success states and retry behavior must remain mutually exclusive.

Acceptance criteria

  • A failed auth-status request never renders “Login password disabled,” “Set a password,” or “Enable login.”
  • A malformed status response is handled as a load error, not as enabled: false.
  • The error state explains that auth status is unknown and offers a touch-accessible Retry action.
  • Retry replaces the error with the correct enabled or disabled controls after a valid response.
  • Component tests cover both valid states, failure, malformed data, and retry without making real network requests.

Metadata

Metadata

Assignees

Labels

area:uiUI components and stylingeffort:mediumEffort: mediummodel:lightModel size: lightplanTracked by /do:replanuxUser experience

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions