From c75b0432591084f029dc7a99436976f9174126ee Mon Sep 17 00:00:00 2001 From: Nicholai Date: Sun, 15 Feb 2026 22:21:54 -0700 Subject: [PATCH] fix(auth): demo cookie no longer overrides real session (#91) * feat(security): add multi-tenancy isolation and demo mode Add org-scoped data isolation across all server actions to prevent cross-org data leakage. Add read-only demo mode with mutation guards on all write endpoints. Multi-tenancy: - org filter on executeDashboardQueries (all query types) - org boundary checks on getChannel, joinChannel - searchMentionableUsers derives org from session - getConversationUsage scoped to user, not org-wide for admins - organizations table, members, org switcher component Demo mode: - /demo route sets strict sameSite cookie - isDemoUser guards on all mutation server actions - demo banner, CTA dialog, and gate components - seed script for demo org data Also: exclude scripts/ from tsconfig (fixes build), add multi-tenancy architecture documentation. * fix(auth): real session takes priority over demo cookie The demo cookie was checked unconditionally before WorkOS auth, so logging in with real credentials after visiting /demo still returned the demo user. Now getCurrentUser() tries WorkOS first and only falls back to the demo cookie when no real session exists. Clears the stale cookie on real login. --------- Co-authored-by: Nicholai --- src/lib/auth.ts | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 1281666..9fcecda 100755 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -69,6 +69,16 @@ export async function getCurrentUser(): Promise { !process.env.WORKOS_API_KEY.includes("placeholder") if (!isWorkOSConfigured) { + // check demo cookie when WorkOS isn't available + try { + const cookieStore = await cookies() + const isDemoSession = + cookieStore.get("compass-demo")?.value === "true" + if (isDemoSession) return DEMO_USER + } catch { + // cookies() may throw in non-request contexts + } + // return mock user for development return { id: "dev-user-1", @@ -89,8 +99,31 @@ export async function getCurrentUser(): Promise { } } + // WorkOS is configured -- try real auth first const session = await withAuth() - if (!session || !session.user) return null + + if (!session || !session.user) { + // no real session; fall back to demo cookie + try { + const cookieStore = await cookies() + const isDemoSession = + cookieStore.get("compass-demo")?.value === "true" + if (isDemoSession) return DEMO_USER + } catch { + // cookies() may throw in non-request contexts + } + return null + } + + // real session exists -- clear stale demo cookie if present + try { + const cookieStore = await cookies() + if (cookieStore.get("compass-demo")) { + cookieStore.delete("compass-demo") + } + } catch { + // cookies() may throw in non-request contexts + } const workosUser = session.user