Multi-account support: per-DID data scoping + clear-on-account-change #31

Closed
opened 2026-06-21 12:34:59 +00:00 by starsetbyte · 0 comments
Owner

Summary

Peregrine supports a single account at a time and keeps account-bound data (Room, draft images on disk, DataStore feed prefs + recent searches) in shared, unscoped stores. #18 (PR #30) added the immediate safety fix — wipe all account-bound data on logout — but that's a blunt instrument: switching accounts loses all cached state and forces a full re-download, and one leak vector remains uncovered. This issue tracks proper multi-account support.

Goals

  1. Per-DID data scoping. Add an accountDid to account-bound Room entities (and key DataStore values by DID), or use a separate DB/DataStore per DID. Lets each account retain its own cached timeline/feeds/drafts/notifications and enables fast switching without re-download.
  2. Clear/scope on account change at the login boundary. Today the only hook is AuthRepository.logout(). Move the "fresh start for a new account" guarantee to the point a session is established: when a newly authenticated DID differs from the stored one, scope (or clear) appropriately.

Remaining leak vector (not covered by #18 / PR #30)

TokenAuthenticator performs a forced logout on refresh failure by calling SessionManager.clearSession() directly — it does not route through AuthRepository.logout(), so the account-data wipe never runs. Sequence: account A's session expires → clearSession() → login screen → account B logs in → B sees A's cached data.

The OAuth login path also bypasses AuthRepository entirely (session saved via OAuthLoginViewModelSessionManager.saveOAuthSession). A login-boundary approach covers app-password login, OAuth login, and post-expiry re-login in one place, whereas the current logout-only hook covers none of those switches.

Acceptance criteria

  • Switching between two accounts preserves each account's cached data (no full re-download).
  • No account ever sees another account's data regardless of how the previous session ended (explicit logout, token-expiry forced logout, or OAuth).
  • App-level settings (theme, dynamic color, migration-dismissed) remain global/device-level.
  • #18 — logout data wipe (PR #30), the immediate safety fix this builds on.
## Summary Peregrine supports a single account at a time and keeps account-bound data (Room, draft images on disk, DataStore feed prefs + recent searches) in shared, unscoped stores. #18 (PR #30) added the immediate safety fix — **wipe** all account-bound data on logout — but that's a blunt instrument: switching accounts loses all cached state and forces a full re-download, and one leak vector remains uncovered. This issue tracks proper multi-account support. ## Goals 1. **Per-DID data scoping.** Add an `accountDid` to account-bound Room entities (and key DataStore values by DID), or use a separate DB/DataStore per DID. Lets each account retain its own cached timeline/feeds/drafts/notifications and enables fast switching without re-download. 2. **Clear/scope on account change at the login boundary.** Today the only hook is `AuthRepository.logout()`. Move the "fresh start for a new account" guarantee to the point a session is established: when a newly authenticated DID differs from the stored one, scope (or clear) appropriately. ## Remaining leak vector (not covered by #18 / PR #30) `TokenAuthenticator` performs a *forced* logout on refresh failure by calling `SessionManager.clearSession()` directly — it does **not** route through `AuthRepository.logout()`, so the account-data wipe never runs. Sequence: account A's session expires → `clearSession()` → login screen → account B logs in → B sees A's cached data. The OAuth login path also bypasses `AuthRepository` entirely (session saved via `OAuthLoginViewModel` → `SessionManager.saveOAuthSession`). A login-boundary approach covers app-password login, OAuth login, and post-expiry re-login in one place, whereas the current logout-only hook covers none of those switches. ## Acceptance criteria - Switching between two accounts preserves each account's cached data (no full re-download). - No account ever sees another account's data regardless of how the previous session ended (explicit logout, token-expiry forced logout, or OAuth). - App-level settings (theme, dynamic color, migration-dismissed) remain global/device-level. ## Related - #18 — logout data wipe (PR #30), the immediate safety fix this builds on.
Sign in to join this conversation.
No description provided.