mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: sign out of one account without dropping the others #1201
Logout switched to the first account in the list. After a restore that is often an account still waiting for sign-in, which has no client, so logout took the full sign-out path and dropped every account. It now switches to the first account that is connected. Boot also counted waiting accounts when deciding whether any account could be restored, so a reload with only waiting accounts left showed a connection error instead of the login page.
This commit is contained in:
@@ -95,3 +95,27 @@ it('stops waiting once the account is signed in', () => {
|
||||
expect(alice).toMatchObject({ label: 'Alice', avatarColor: '#16a34a', isConnected: true });
|
||||
expect(alice?.awaitingSignIn).toBeUndefined();
|
||||
});
|
||||
|
||||
it('signs out to an account that is signed in, not to one awaiting sign-in', async () => {
|
||||
restoreAccounts(archived);
|
||||
useAccountStore.setState((s) => ({
|
||||
accounts: [...s.accounts, { ...signedIn, id: 'carol@x.test@mail.x.test', username: 'carol@x.test', email: 'carol@x.test', label: 'Carol', cookieSlot: 2, isDefault: false }] as never,
|
||||
}));
|
||||
await useAuthStore.getState().checkAuth();
|
||||
// Wait for the background restore of the other remembered account.
|
||||
await vi.waitFor(() => expect(useAccountStore.getState().getAccountById('carol@x.test@mail.x.test')?.isConnected).toBe(true));
|
||||
|
||||
await useAuthStore.getState().logout();
|
||||
expect(useAuthStore.getState().activeAccountId).toBe('carol@x.test@mail.x.test');
|
||||
expect(useAuthStore.getState().isAuthenticated).toBe(true);
|
||||
expect(useAccountStore.getState().accounts.map(a => a.id)).toEqual(['alice@x.test@mail.x.test', 'carol@x.test@mail.x.test']);
|
||||
});
|
||||
|
||||
it('treats a browser left with only accounts awaiting sign-in as signed out, not as a connection failure', async () => {
|
||||
useAccountStore.setState({ accounts: [{ ...signedIn, rememberMe: false }] as never });
|
||||
restoreAccounts(archived.slice(1));
|
||||
await useAuthStore.getState().checkAuth();
|
||||
expect(useAuthStore.getState().isAuthenticated).toBe(false);
|
||||
expect(useAuthStore.getState().error).not.toBe('connection_failed');
|
||||
expect(useAccountStore.getState().accounts.map(a => a.id)).toEqual(['alice@x.test@mail.x.test']);
|
||||
});
|
||||
|
||||
+14
-3
@@ -2112,7 +2112,13 @@ export const useAuthStore = create<AuthState>()(
|
||||
|
||||
// Check if there are remaining accounts to switch to. Read the store
|
||||
// afresh: `accountStore` is the state from before removeAccount().
|
||||
const nextAccount = wasDemoMode ? undefined : useAccountStore.getState().accounts[0];
|
||||
// Prefer an account that is signed in here: one restored from the
|
||||
// account list backup (or otherwise not connected) has no client, and
|
||||
// picking it signs out every account left.
|
||||
const remainingAccounts = useAccountStore.getState().accounts;
|
||||
const nextAccount = wasDemoMode
|
||||
? undefined
|
||||
: remainingAccounts.find((a) => clients.has(a.id)) ?? remainingAccounts[0];
|
||||
const droppedAccounts: AccountEntry[] = [];
|
||||
|
||||
if (nextAccount) {
|
||||
@@ -2762,8 +2768,13 @@ export const useAuthStore = create<AuthState>()(
|
||||
}
|
||||
}
|
||||
|
||||
// No accounts could be restored
|
||||
if (accounts.some((account) => accountStore.getAccountById(account.id))) {
|
||||
// No accounts could be restored. Accounts restored from the account
|
||||
// list backup were never signed in here, so they say nothing about
|
||||
// the connection.
|
||||
if (accounts.some((account) => {
|
||||
const entry = accountStore.getAccountById(account.id);
|
||||
return entry && !entry.awaitingSignIn;
|
||||
})) {
|
||||
set({
|
||||
isAuthenticated: false,
|
||||
isLoading: false,
|
||||
|
||||
Reference in New Issue
Block a user