diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx index 520fbb56..b010dfa5 100644 --- a/app/[locale]/page.tsx +++ b/app/[locale]/page.tsx @@ -471,7 +471,9 @@ export default function Home() { }; const handleEditDraft = (email?: Email) => { - const draft = email || selectedEmail; + // Guard: when used directly as an onClick handler, the click event is passed + // as the first argument. Detect this and fall back to selectedEmail. + const draft = (email && 'mailboxIds' in email) ? email : selectedEmail; if (!draft) return; const bodyText = draft.bodyValues ? Object.values(draft.bodyValues).map(v => v.value).join('\n') diff --git a/app/api/settings/route.ts b/app/api/settings/route.ts index ccb3eb26..ed688c5f 100644 --- a/app/api/settings/route.ts +++ b/app/api/settings/route.ts @@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { cookies } from 'next/headers'; import { logger } from '@/lib/logger'; import { decryptSession } from '@/lib/auth/crypto'; -import { SESSION_COOKIE } from '@/lib/auth/session-cookie'; +import { sessionCookieName } from '@/lib/auth/session-cookie'; import { saveUserSettings, loadUserSettings, deleteUserSettings } from '@/lib/settings-sync'; function isEnabled(): boolean { @@ -10,19 +10,30 @@ function isEnabled(): boolean { } /** - * Verify identity against the session cookie if available. - * Returns true if no session cookie exists (can't verify) or if identity matches. - * Returns false if session cookie exists but identity doesn't match. + * Verify identity against session cookies across all account slots. + * With multi-account, the requesting account may be on any slot (0-4). + * Returns true if any slot matches OR if no session cookies exist at all. */ async function verifyIdentity(username: string, serverUrl: string): Promise { const cookieStore = await cookies(); - const sessionToken = cookieStore.get(SESSION_COOKIE)?.value; - if (!sessionToken) return true; // No session cookie, can't verify (same-origin protection applies) + let hasAnyCookie = false; - const session = decryptSession(sessionToken); - if (!session) return true; // Invalid session cookie, skip verification + for (let slot = 0; slot <= 4; slot++) { + const token = cookieStore.get(sessionCookieName(slot))?.value; + if (!token) continue; + hasAnyCookie = true; - return session.username === username && session.serverUrl === serverUrl; + const session = decryptSession(token); + if (session && session.username === username && session.serverUrl === serverUrl) { + return true; // Found a matching slot + } + } + + // No cookies at all → can't verify, allow (same-origin protection applies) + if (!hasAnyCookie) return true; + + // Cookies exist but none matched → identity mismatch + return false; } export async function GET(request: NextRequest) { diff --git a/stores/auth-store.ts b/stores/auth-store.ts index e9ea638f..34f6db15 100644 --- a/stores/auth-store.ts +++ b/stores/auth-store.ts @@ -691,7 +691,9 @@ export const useAuthStore = create()( const targetAccount = accountStore.getAccountById(accountId); if (!targetAccount) return; - set({ isLoading: true }); + // Null out the client immediately so the page doesn't fire data-loading + // effects with the old client while stores are being cleared. + set({ isLoading: true, client: null }); // Snapshot current account if (state.activeAccountId) { @@ -827,7 +829,9 @@ export const useAuthStore = create()( // Multi-account restoration: restore all registered accounts if (accounts.length > 0) { - set({ isLoading: true }); + // Null out client so the page doesn't fire data-loading effects + // with a stale client reference while we're restoring accounts. + set({ isLoading: true, client: null }); // Determine which account to activate first const defaultAccount = accountStore.getDefaultAccount();