Soft logout keeps local data; MAS front hides register and reset #145

Merged
void merged 5 commits from void/mas-client-fixes into dev 2026-11-13 19:32:10 +00:00
Member

Two client fixes for the MAS window (follow-ups ratatoskr:follow_up:mas-audit-sessions, mas-audit-gaps-and-oidc-scope; ask chat-s7472-7473). Not merged, not deployed.

1. Soft logout keeps local data. SessionLoggedOut with data.soft_logout === true no longer wipes: the client stops, only the access token is removed, and a small record {userId, deviceId} (state/softLogout.ts) is kept. The sign-in page then says the session expired, fixes the username to that user, and sends device_id on both password and m.login.token requests. Same user and device back: stores kept, record cleared. Different user or device (also new registration): one wipeLocalData() first. "Sign in as someone else" is the hard path. Anything that is not soft_logout === true stays hard. The listener is idempotent (the SDK emits once per failing request): createLogoutHandler, tested with repeated events.
One wipe owner: logout, hard listener, clearLoginData and mismatch share wipeLocalData, which now also deletes every IndexedDB database (a superset of the old wipe; it also clears the service worker's push-session DB) and swallows clearStores errors.

2. MAS front. Detected pre-login from autodiscovery org.matrix.msc2965.authentication (account ?? issuer, https or localhost only; the devices page uses the same preference post-login). Under MAS: Forgot-password link hidden; ResetPassword and, when no registration entry is configured, RegisterRoute and the bare Register link show a pointer to the account page. A configured registrationEntry (with its training notice) is deliberately kept, since it is where newcomers see the consent text. Non-MAS render paths unchanged.

Soul pass: found a duplicate-event wipe regression, the consent regression and a register-path stale record; all fixed in the second commit. Hands' behavior tests cover the handler, settleLocalData, masAccountUrl, RegisterRoute MAS cases. Remaining guards are source-level (repo has no React testing library). Full suite: 1382 pass, 1 timeout in features/scene/index.test.ts (passes alone; load flake, not touched here).

Could not be seen without a real MAS (for Eureka's scratch MAS):

  • Does MAS honor a client-supplied device_id on compat m.login.password and m.login.token? If it hands back another device, the keep path never fires (mismatch wipe applies).
  • Does MAS answer an expired or revoked compat token with soft_logout: true? If it sends M_UNKNOWN_TOKEN without it, every logout stays hard (today's behavior).
  • Does /.well-known/matrix/client advertise org.matrix.msc2965.authentication with account or issuer? Without it the MAS front never activates.
  • Is the advertised account URL the right landing page for register/recover?
  • Do old tokens survive syn2mas migration (a dead one arrives as a hard logout)?
  • SessionLoggedOut carrying the MatrixError with data.soft_logout is read from the SDK source, not a live emit.
  • Browser behavior of the IndexedDB wipe, indexedDB.databases() fallback and the reload flow: no real-browser run.

🤖 Generated with Claude Code

Two client fixes for the MAS window (follow-ups `ratatoskr:follow_up:mas-audit-sessions`, `mas-audit-gaps-and-oidc-scope`; ask `chat-s7472-7473`). Not merged, not deployed. **1. Soft logout keeps local data.** `SessionLoggedOut` with `data.soft_logout === true` no longer wipes: the client stops, only the access token is removed, and a small record {userId, deviceId} (`state/softLogout.ts`) is kept. The sign-in page then says the session expired, fixes the username to that user, and sends `device_id` on both password and `m.login.token` requests. Same user and device back: stores kept, record cleared. Different user or device (also new registration): one `wipeLocalData()` first. "Sign in as someone else" is the hard path. Anything that is not `soft_logout === true` stays hard. The listener is idempotent (the SDK emits once per failing request): `createLogoutHandler`, tested with repeated events. One wipe owner: logout, hard listener, `clearLoginData` and mismatch share `wipeLocalData`, which now also deletes every IndexedDB database (a superset of the old wipe; it also clears the service worker's push-session DB) and swallows `clearStores` errors. **2. MAS front.** Detected pre-login from autodiscovery `org.matrix.msc2965.authentication` (`account ?? issuer`, https or localhost only; the devices page uses the same preference post-login). Under MAS: Forgot-password link hidden; `ResetPassword` and, when no `registration` entry is configured, `RegisterRoute` and the bare Register link show a pointer to the account page. A configured `registrationEntry` (with its training notice) is deliberately kept, since it is where newcomers see the consent text. Non-MAS render paths unchanged. Soul pass: found a duplicate-event wipe regression, the consent regression and a register-path stale record; all fixed in the second commit. Hands' behavior tests cover the handler, `settleLocalData`, `masAccountUrl`, RegisterRoute MAS cases. Remaining guards are source-level (repo has no React testing library). Full suite: 1382 pass, 1 timeout in `features/scene/index.test.ts` (passes alone; load flake, not touched here). **Could not be seen without a real MAS (for Eureka's scratch MAS):** - Does MAS honor a client-supplied `device_id` on compat `m.login.password` and `m.login.token`? If it hands back another device, the keep path never fires (mismatch wipe applies). - Does MAS answer an expired or revoked compat token with `soft_logout: true`? If it sends `M_UNKNOWN_TOKEN` without it, every logout stays hard (today's behavior). - Does `/.well-known/matrix/client` advertise `org.matrix.msc2965.authentication` with `account` or `issuer`? Without it the MAS front never activates. - Is the advertised account URL the right landing page for register/recover? - Do old tokens survive syn2mas migration (a dead one arrives as a hard logout)? - `SessionLoggedOut` carrying the MatrixError with `data.soft_logout` is read from the SDK source, not a live emit. - Browser behavior of the IndexedDB wipe, `indexedDB.databases()` fallback and the reload flow: no real-browser run. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Soft (soft_logout === true) drops only the access token and records the device; sign-in is fixed to that user and sends device_id; same user and device keeps the stores, anything else wipes via the one wipeLocalData. Hard logout still wipes. When discovery advertises an msc2965 account or issuer, the register and forgot-password entries give way to a notice pointing at the account page.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Soul fixes: idempotent logout handler, keep register consent entry under MAS, settle stores on register
Some checks failed
Build pull request / Build pull request (pull_request) Has been cancelled
3a5258c3ef
First SessionLoggedOut decides; later events are ignored so a second soft event cannot fall into the wipe. Under MAS a configured registrationEntry (with its training notice) is kept; the MAS notice replaces only the in-client form and bare link. Registration goes through the same keep-or-wipe settle as login. Soft record drops its unused baseUrl. One shared https-or-localhost predicate.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Second Soul pass: soft event without a session wipes, wipe never rejects, rendered MAS tests
Some checks failed
Build pull request / Build pull request (pull_request) Has been cancelled
31d58075db
wipeLocalData moves to its own module with injectable ports: per-database failures are contained and localStorage clears in a finally. A soft logout with no session to remember is treated as hard. Login/PasswordLoginForm/ResetPassword MAS behavior is now tested by rendering. Login and register completion settle once per response.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
wipeLocalData: bounded clearStores, storage clear that cannot reject; tests that bite
Some checks failed
Build pull request / Build pull request (pull_request) Has been cancelled
8fb95402f5
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
wipeLocalData test: lint-clean never-settling store
All checks were successful
Build pull request / Build pull request (pull_request) Successful in 3m23s
cd30c3dc40
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
void merged commit 229591e22c into dev 2026-11-13 19:32:10 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
GameCult/Ratatoskr!145
No description provided.