Files
Toju/emergency-fix/04-auth-login-bugs.md
T
myxelium e49b3ec112 chore: dev-stack switches, shared e2e harness, and desktop shell rules
- `LIVE_RELOAD=false npm run dev` keeps the renderer alive across a machine
  suspend; the reload client otherwise destroys the session under test.
- `dev-peer.sh` plus a separate userdata dir runs a second local peer.
- `tools/voice-probe.js` samples peer state and RTP counters from a live
  window, persisting to localStorage so a renderer reload cannot erase it.
- e2e helpers for voice pairs, peer-role election, and a TURN relay.
- Electron single-instance and dev-client-load decisions move into rules
  files with colocated specs.
2026-08-14 03:19:29 +02:00

5.0 KiB
Raw Blame History

04 — Auth & login bugs

User theme: “Im logged in but it keeps asking me to log in / authorize.”
Severity: P0 — blocks multi-server chat, voice, presence.
Existing research: agents-docs/user-stories/silent-cross-signal-server-auth.md (do not redo; implement after interview).


How auth is supposed to work (user view)

  1. Register or log in once on a home signal server.
  2. Stay signed in across app restarts (desktop).
  3. When touching another signal server (join, invite, create room, activate endpoint): the app silently creates or reuses an account there.
  4. You only see a login form again if:
    • true home session expired / missing token, or
    • last-resort: username collision exhaustion / user clicked Sign in in Network settings.
  5. Offline / dead endpoints never bounce you to authorize login.

Settings → Network may show Authorized / Needs sign-in as diagnostics — not as the default path for normal joins.


How auth is supposed to work (system)

Concept Role
Home credential Token + user id for homeSignalServerUrl
Foreign credential Separate user id + token per URL in SignalServerCredentialStore
Provision secret Password used only for auto register/login on foreign hosts
ensureProvisioned Register-or-login with secret; suffix username on collision
ensureCredentialForServerUrl Gate before foreign room connect; navigate authorize only for collision or no-provision-secret when endpoint online
authorize mode Manual login that upserts foreign credential without resetting home profile
auth_required vs auth_error Race vs rejected token — must not falsely expire home on foreign races

Authorize nav rule: shouldNavigateToAuthorizeSignalServer in signal-server-authorize.rules.ts.


Failure modes (code-backed)

A — Missing provision secret → authorize UI (primary)

Symptom: Logged-in user opens foreign room/invite → /login?mode=authorize.

Mechanism

  1. ensureProvisioned{ kind: 'skipped', reason: 'no-provision-secret' }.
  2. ensureCredentialForServerUrl navigates to authorize.
  3. Authorize mode does not auto-leave when currentUser exists → stuck prompt.

Why secret missing

  • Created only in prepareAuthenticatedUserStorage when homeSignalServerUrl + loginResponse present.
  • Session restore calls ensureProvisioned but does not ensure secret exists first.
  • Web: sessionStorage secret dies with tab.
  • Old installs / wiped Electron userData/provision-secrets/.

Files

  • signal-server-auth.service.ts, signal-server-authorize.service.ts
  • provision-secret-store.service.ts, electron/api/provision-secret-store.ts
  • users.effects.ts

B — Username collision exhaustion

All register candidates 409 + login 401 → collision → authorize. Rare but real on crowded foreign servers.

C — False home session expiry

signalServerAuthFailed$ may SESSION_EXPIRED → full /login when home classification / retry budget wrong. Distinguish auth_required (re-identify) vs auth_error (clear credential; foreign re-provision vs home expire).

D — Credential missing → unauthenticated join → invisible user

Without token, socket never identifies; join_server dropped; user alone; later gates open authorize. Cascades into “messages not seen” and “voice empty”.

E — Opportunistic provision swallows errors

ensureProvisioned(...).catch(() => undefined) on health/startup → failure deferred until mid-join authorize popup.

F — Others?


Doc / README lies in this area

Claim Reality
Auth domain README sequence: POST /api/auth/login Real paths: /api/users/login, /api/users/register (AuthenticationService)
Feature doc: offline must not open authorize Code path exists; still fails open on missing secret when “online”

Fix directions (interview choices — not approved)

From user story (recommend A):

Option Idea
A On restore / before foreign provision, always ensureHomeProvisionSecret; keep authorize only for collision / manual
B Never navigate on no-provision-secret; toast + Network badge
C Durable web secret (not sessionStorage)
D Stronger unique usernames before collision UI

Proof of done

  1. Two live signal servers: home register on A → join room on B → no /login navigation.
  2. Full Electron restart → foreign rejoin still silent.
  3. Offline foreign URL → no authorize navigation.
  4. Focused tests: missing secret on restore → secret created → provision → Router.navigate(['/login']) never called.
  5. Foreign auth_error with valid home → re-provision, not SESSION_EXPIRED.

Agent scope

  • Default: toju-app/domains/authentication, store/users, store/rooms/room-signaling-connection.ts, server-directory call sites.
  • Electron: provision-secret store/IPC only if persistence fix needs it.
  • Ask before deep server/ auth changes.