From fce2ccb9d13ad0e9907b89bcbd3ea8dc284d9dc9 Mon Sep 17 00:00:00 2001 From: Vorflux AI Date: Sun, 23 Aug 2026 22:35:52 +0000 Subject: [PATCH 1/7] feat(web): select organization during plugin auth --- apps/web/app/auth/connect/page.tsx | 443 ++++++++++++++++++++--------- packages/lib/auth-context.tsx | 9 +- 2 files changed, 318 insertions(+), 134 deletions(-) diff --git a/apps/web/app/auth/connect/page.tsx b/apps/web/app/auth/connect/page.tsx index febd27600..f4e52289f 100644 --- a/apps/web/app/auth/connect/page.tsx +++ b/apps/web/app/auth/connect/page.tsx @@ -3,11 +3,19 @@ import { useAuth } from "@lib/auth-context" import { useSession } from "@lib/auth" import { cn } from "@lib/utils" +import { Logo } from "@ui/assets/Logo" import { dmSans125ClassName } from "@/lib/fonts" -import { ArrowRight, XCircle } from "lucide-react" +import { ArrowLeft, ArrowRight, LoaderIcon, XCircle } from "lucide-react" import Image from "next/image" import { useRouter, useSearchParams } from "next/navigation" -import { Suspense, useEffect, useMemo, useState } from "react" +import { + Suspense, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react" import { PENDING_CONNECT_URL_KEY } from "@/lib/constants" @@ -172,7 +180,7 @@ function PluginLogoStack({ clients }: { clients: string[] }) { ) } -type Status = "loading" | "creating" | "success" | "error" +type Status = "loading" | "selection" | "approval" | "creating" | "success" const pageWrapperClass = "flex items-center justify-center min-h-screen bg-background p-4" @@ -185,9 +193,16 @@ function AuthConnectContent() { const params = useSearchParams() const router = useRouter() const { data: session, isPending } = useSession() - const { org, organizations, isRestoring } = useAuth() + const { organizations, isRestoring, setActiveOrg } = useAuth() const [status, setStatus] = useState("loading") const [error, setError] = useState(null) + const [selectedOrgId, setSelectedOrgId] = useState(null) + const [switchingOrgId, setSwitchingOrgId] = useState(null) + const switchingOrgIdRef = useRef(null) + const autoAttemptedOrgId = useRef(null) + const listRef = useRef(null) + const [canScrollUp, setCanScrollUp] = useState(false) + const [canScrollDown, setCanScrollDown] = useState(false) const callback = params.get("callback") const client = params.get("client") @@ -214,10 +229,22 @@ function AuthConnectContent() { requestedClients.length === 1 && validClient ? PLUGIN_INFO[validClient] : null + const requestError = useMemo(() => { + if (!callback) return "Missing callback parameter." + if (!isValidLocalhostCallback(callback)) return "Invalid callback URL." + if (invalidClients.length > 0) { + return `Unsupported plugin requested: ${invalidClients.join(", ")}.` + } + if (requestedClients.length === 0) return "Invalid or missing client." + return null + }, [callback, invalidClients, requestedClients.length]) + const selectedOrg = + organizations?.find((organization) => organization.id === selectedOrgId) ?? + null + const multiOrg = (organizations?.length ?? 0) > 1 - // Redirect new users (logged in but no organization) to onboarding. - // Store the current connect URL so onboarding can redirect back here. const shouldRedirectToOnboarding = + !requestError && !isPending && !isRestoring && !!session && @@ -225,6 +252,7 @@ function AuthConnectContent() { organizations.length === 0 useEffect(() => { + if (requestError) return if (isPending || isRestoring) return if (!session) return if (organizations === null) return // orgs query still pending @@ -236,41 +264,103 @@ function AuthConnectContent() { console.warn("Failed to access sessionStorage for pending connect URL", e) } router.replace("/onboarding") - }, [isPending, isRestoring, session, organizations, router]) + }, [isPending, isRestoring, session, organizations, router, requestError]) + + const selectOrganization = useCallback( + async (organization: NonNullable[number]) => { + if (switchingOrgIdRef.current) return + + setError(null) + switchingOrgIdRef.current = organization.id + setSwitchingOrgId(organization.id) + try { + await setActiveOrg(organization.slug) + setSelectedOrgId(organization.id) + setStatus("approval") + } catch (err) { + console.error("Failed to switch organization:", err) + setError("Couldn't switch to that organization. Try again.") + setStatus("selection") + } finally { + switchingOrgIdRef.current = null + setSwitchingOrgId(null) + } + }, + [setActiveOrg], + ) - async function handleConnect() { - if (!callback) { - setStatus("error") - setError("Missing callback parameter.") + useEffect(() => { + if (requestError || isPending || isRestoring || organizations === null) return - } - if (!isValidLocalhostCallback(callback)) { - setStatus("error") - setError("Invalid callback URL.") + if (!session || organizations.length === 0 || status !== "loading") return + if (organizations.length > 1) { + setStatus("selection") return } - if (invalidClients.length > 0) { - setStatus("error") - setError(`Unsupported plugin requested: ${invalidClients.join(", ")}.`) + const onlyOrganization = organizations[0] + if ( + !onlyOrganization || + autoAttemptedOrgId.current === onlyOrganization.id + ) { return } - if (requestedClients.length === 0) { - setStatus("error") - setError("Invalid or missing client.") + autoAttemptedOrgId.current = onlyOrganization.id + setStatus("selection") + void selectOrganization(onlyOrganization) + }, [ + requestError, + isPending, + isRestoring, + organizations, + session, + status, + selectOrganization, + ]) + + useEffect(() => { + if (status !== "approval" || !selectedOrgId || organizations === null) + return + if ( + organizations.some((organization) => organization.id === selectedOrgId) + ) { return } - if (!session || !org) { - setStatus("error") + setSelectedOrgId(null) + setError("That organization is no longer available. Choose another one.") + setStatus("selection") + }, [organizations, selectedOrgId, status]) + + const measureFades = useCallback((element: HTMLDivElement | null) => { + if (!element) return + setCanScrollUp(element.scrollTop > 8) + setCanScrollDown( + element.scrollTop + element.clientHeight < element.scrollHeight - 8, + ) + }, []) + + useEffect(() => { + if (status !== "selection") return + measureFades(listRef.current) + }, [measureFades, status]) + + async function handleConnect() { + if (requestError || !callback) return + if (!session || !selectedOrg) { setError( - "Your account is not fully set up yet. Please complete onboarding first.", + selectedOrgId + ? "That organization is no longer available. Choose another one." + : "Select an organization before approving the connection.", ) + setStatus(multiOrg ? "selection" : "approval") return } try { + setError(null) setStatus("creating") const fetchParams = new URLSearchParams({ callback }) fetchParams.set("client", requestedClients[0] ?? "") + fetchParams.set("orgId", selectedOrg.id) const res = await fetch(`${API_URL}/v3/auth/key?${fetchParams}`, { credentials: "include", @@ -306,149 +396,239 @@ function AuthConnectContent() { window.location.href = redirectUrl.toString() } catch (err) { console.error("Failed to get API key:", err) - setStatus("error") setError(err instanceof Error ? err.message : "Failed to get API key") + setStatus("approval") } } - // Show a spinner while session/org data is loading or while we're about - // to redirect to onboarding (prevents a brief flash of the connect card). const isAuthLoading = isPending || isRestoring || organizations === null - useEffect(() => { - if (status !== "loading") return - if (rawRequestedClients.length === 0) { - setStatus("error") - setError("Invalid or missing client.") - return - } - if (invalidClients.length > 0) { - setStatus("error") - setError(`Unsupported plugin requested: ${invalidClients.join(", ")}.`) - } - }, [invalidClients, rawRequestedClients.length, status]) - - if (isAuthLoading || shouldRedirectToOnboarding) { - return ( -
-
-
- ) - } - - if (status === "loading") { + if (requestError) { return (
-
- -
+
+ +

- Connect {displayName} + Connection failed

- {pluginInfo?.description ?? - (requestedClients.length > 1 - ? "Use one Supermemory account across these plugins." - : `Use your Supermemory account with ${displayName}.`)} + {requestError}

- -
+
+
+ ) + } - + ))} +
+
- Approve Connection -
- + /> +
+ {error && ( +

+ {error} +

+ )}
) } - if (status === "error") { + + if (status === "approval" || status === "creating") { + const creating = status === "creating" return ( -
-
-
- -
-

- Connection failed -

-

- {error} +

+
+
+
+ +

+ Connect {displayName} +

+

+ {pluginInfo?.description ?? + (requestedClients.length > 1 + ? "Use one Supermemory account across these plugins." + : `Use your Supermemory account with ${displayName}.`)} +

+
+
    + {(pluginInfo?.features ?? MULTI_PLUGIN_FEATURES).map((feature) => ( +
  • + + {feature} +
  • + ))} +
+
+
+
+ + Connecting to + +

+ {selectedOrg?.name ?? "Organization unavailable"}

- -
+ {multiOrg && ( - - Go to app - -
+ )} +
+
+ {error && ( +

+ {error} +

+ )} +
+
@@ -460,7 +640,6 @@ function AuthConnectContent() {

- {status === "creating" && `Connecting ${displayName}…`} {status === "success" && `Success! Redirecting back to ${displayName}…`}

diff --git a/packages/lib/auth-context.tsx b/packages/lib/auth-context.tsx index acd15e882..7234975e2 100644 --- a/packages/lib/auth-context.tsx +++ b/packages/lib/auth-context.tsx @@ -75,8 +75,13 @@ export function AuthProvider({ children }: { children: ReactNode }) { const res = await authClient.organization.setActive({ organizationSlug: slug, }) - setOrg(res?.data ?? null) - localStorage.setItem(STORAGE_KEY, slug) + if (res.error || !res.data) { + throw new Error(res.error?.message ?? "Failed to switch organization") + } + try { + localStorage.setItem(STORAGE_KEY, slug) + } catch {} + setOrg(res.data) }, []) const clearActiveOrg = useCallback(async () => { From 40519683c8ef350cc3f7681a12fa99951ba83fc2 Mon Sep 17 00:00:00 2001 From: Ishaan Gupta Date: Mon, 24 Aug 2026 12:17:28 +0530 Subject: [PATCH 2/7] fix plugin organization authorization flow --- apps/web/app/auth/connect/page.tsx | 131 +++++++++++++++-------------- packages/lib/auth-context.tsx | 9 +- 2 files changed, 72 insertions(+), 68 deletions(-) diff --git a/apps/web/app/auth/connect/page.tsx b/apps/web/app/auth/connect/page.tsx index f4e52289f..7e9dd5cfa 100644 --- a/apps/web/app/auth/connect/page.tsx +++ b/apps/web/app/auth/connect/page.tsx @@ -193,13 +193,10 @@ function AuthConnectContent() { const params = useSearchParams() const router = useRouter() const { data: session, isPending } = useSession() - const { organizations, isRestoring, setActiveOrg } = useAuth() + const { organizations, isRestoring } = useAuth() const [status, setStatus] = useState("loading") const [error, setError] = useState(null) const [selectedOrgId, setSelectedOrgId] = useState(null) - const [switchingOrgId, setSwitchingOrgId] = useState(null) - const switchingOrgIdRef = useRef(null) - const autoAttemptedOrgId = useRef(null) const listRef = useRef(null) const [canScrollUp, setCanScrollUp] = useState(false) const [canScrollDown, setCanScrollDown] = useState(false) @@ -267,26 +264,12 @@ function AuthConnectContent() { }, [isPending, isRestoring, session, organizations, router, requestError]) const selectOrganization = useCallback( - async (organization: NonNullable[number]) => { - if (switchingOrgIdRef.current) return - + (organization: NonNullable[number]) => { setError(null) - switchingOrgIdRef.current = organization.id - setSwitchingOrgId(organization.id) - try { - await setActiveOrg(organization.slug) - setSelectedOrgId(organization.id) - setStatus("approval") - } catch (err) { - console.error("Failed to switch organization:", err) - setError("Couldn't switch to that organization. Try again.") - setStatus("selection") - } finally { - switchingOrgIdRef.current = null - setSwitchingOrgId(null) - } + setSelectedOrgId(organization.id) + setStatus("approval") }, - [setActiveOrg], + [], ) useEffect(() => { @@ -298,15 +281,7 @@ function AuthConnectContent() { return } const onlyOrganization = organizations[0] - if ( - !onlyOrganization || - autoAttemptedOrgId.current === onlyOrganization.id - ) { - return - } - autoAttemptedOrgId.current = onlyOrganization.id - setStatus("selection") - void selectOrganization(onlyOrganization) + if (onlyOrganization) selectOrganization(onlyOrganization) }, [ requestError, isPending, @@ -358,39 +333,77 @@ function AuthConnectContent() { try { setError(null) setStatus("creating") - const fetchParams = new URLSearchParams({ callback }) - fetchParams.set("client", requestedClients[0] ?? "") - fetchParams.set("orgId", selectedOrg.id) - - const res = await fetch(`${API_URL}/v3/auth/key?${fetchParams}`, { - credentials: "include", - }) - - if (!res.ok) { - const errorData = (await res.json().catch(() => ({}))) as { - message?: string + const keyResults = await Promise.allSettled( + requestedClients.map(async (requestedClient) => { + const fetchParams = new URLSearchParams({ + callback, + client: requestedClient, + orgId: selectedOrg.id, + }) + const res = await fetch(`${API_URL}/v3/auth/key?${fetchParams}`, { + credentials: "include", + }) + + if (!res.ok) { + const errorData = (await res.json().catch(() => ({}))) as { + message?: string + } + throw new Error(errorData.message || "Failed to get API key") + } + + const data = (await res.json()) as { + key: string + organization?: { id: string } + } + const expectedKeyPrefix = `sm_${selectedOrg.id}_` + if ( + (data.organization && data.organization.id !== selectedOrg.id) || + !data.key.startsWith(expectedKeyPrefix) + ) { + throw new Error( + "The server did not create a key for the selected organization. Try again shortly.", + ) + } + + return [requestedClient, data.key] as const + }), + ) + const keys: Record = {} + const errors: Record = {} + for (const [index, result] of keyResults.entries()) { + const requestedClient = requestedClients[index] + if (!requestedClient) continue + if (result.status === "fulfilled") { + keys[result.value[0]] = result.value[1] + } else { + errors[requestedClient] = + result.reason instanceof Error + ? result.reason.message + : "Failed to get API key" } - throw new Error(errorData.message || "Failed to get API key") } - const data = (await res.json()) as { key: string } + if (!hasClientList && Object.keys(errors).length > 0) { + throw new Error(errors[requestedClients[0] ?? ""]) + } + if (Object.keys(keys).length === 0) { + throw new Error( + Object.values(errors)[0] ?? "Failed to get plugin API keys", + ) + } setStatus("success") const redirectUrl = new URL(callback) if (hasClientList) { + redirectUrl.searchParams.set("keys", encodeBase64UrlJson(keys)) + if (Object.keys(errors).length > 0) { + redirectUrl.searchParams.set("errors", encodeBase64UrlJson(errors)) + } + } else { redirectUrl.searchParams.set( - "keys", - encodeBase64UrlJson( - Object.fromEntries( - requestedClients.map((requestedClient) => [ - requestedClient, - data.key, - ]), - ), - ), + "apikey", + keys[requestedClients[0] ?? ""] ?? "", ) - } else { - redirectUrl.searchParams.set("apikey", data.key) } redirectUrl.searchParams.set("api_url", API_URL) window.location.href = redirectUrl.toString() @@ -482,10 +495,9 @@ function AuthConnectContent() { {organizations?.map((organization) => ( ))}
diff --git a/packages/lib/auth-context.tsx b/packages/lib/auth-context.tsx index 7234975e2..acd15e882 100644 --- a/packages/lib/auth-context.tsx +++ b/packages/lib/auth-context.tsx @@ -75,13 +75,8 @@ export function AuthProvider({ children }: { children: ReactNode }) { const res = await authClient.organization.setActive({ organizationSlug: slug, }) - if (res.error || !res.data) { - throw new Error(res.error?.message ?? "Failed to switch organization") - } - try { - localStorage.setItem(STORAGE_KEY, slug) - } catch {} - setOrg(res.data) + setOrg(res?.data ?? null) + localStorage.setItem(STORAGE_KEY, slug) }, []) const clearActiveOrg = useCallback(async () => { From 22cf75912db6060e7ed4ec0a36c783899d1e085f Mon Sep 17 00:00:00 2001 From: Ishaan Gupta Date: Mon, 24 Aug 2026 12:33:33 +0530 Subject: [PATCH 3/7] handle expired plugin connect sessions --- apps/web/app/auth/connect/page.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/web/app/auth/connect/page.tsx b/apps/web/app/auth/connect/page.tsx index 7e9dd5cfa..8ca89f37a 100644 --- a/apps/web/app/auth/connect/page.tsx +++ b/apps/web/app/auth/connect/page.tsx @@ -248,6 +248,13 @@ function AuthConnectContent() { Array.isArray(organizations) && organizations.length === 0 + useEffect(() => { + if (requestError || isPending || isRestoring || session) return + router.replace( + `/login?redirect=${encodeURIComponent(window.location.href)}`, + ) + }, [isPending, isRestoring, requestError, router, session]) + useEffect(() => { if (requestError) return if (isPending || isRestoring) return From 86fbb823a2037d12e4a26c6a74713124d09b0cd5 Mon Sep 17 00:00:00 2001 From: Ishaan Gupta Date: Mon, 24 Aug 2026 19:05:34 +0530 Subject: [PATCH 4/7] refine plugin organization authorization UX --- apps/web/app/auth/connect/page.tsx | 95 +++++++++++++++++++----------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/apps/web/app/auth/connect/page.tsx b/apps/web/app/auth/connect/page.tsx index 8ca89f37a..bbd6bb95d 100644 --- a/apps/web/app/auth/connect/page.tsx +++ b/apps/web/app/auth/connect/page.tsx @@ -5,7 +5,7 @@ import { useSession } from "@lib/auth" import { cn } from "@lib/utils" import { Logo } from "@ui/assets/Logo" import { dmSans125ClassName } from "@/lib/fonts" -import { ArrowLeft, ArrowRight, LoaderIcon, XCircle } from "lucide-react" +import { ArrowRight, LoaderIcon, XCircle } from "lucide-react" import Image from "next/image" import { useRouter, useSearchParams } from "next/navigation" import { @@ -193,7 +193,7 @@ function AuthConnectContent() { const params = useSearchParams() const router = useRouter() const { data: session, isPending } = useSession() - const { organizations, isRestoring } = useAuth() + const { org, organizations, isRestoring } = useAuth() const [status, setStatus] = useState("loading") const [error, setError] = useState(null) const [selectedOrgId, setSelectedOrgId] = useState(null) @@ -205,6 +205,7 @@ function AuthConnectContent() { const client = params.get("client") const clientsParam = params.get("clients") const hasClientList = params.has("clients") + const isSwitchMode = params.get("mode") === "switch_organization" const rawRequestedClients = useMemo( () => (clientsParam !== null ? clientsParam.split(",") : client ? [client] : []) @@ -270,33 +271,31 @@ function AuthConnectContent() { router.replace("/onboarding") }, [isPending, isRestoring, session, organizations, router, requestError]) - const selectOrganization = useCallback( - (organization: NonNullable[number]) => { - setError(null) - setSelectedOrgId(organization.id) - setStatus("approval") - }, - [], - ) - useEffect(() => { if (requestError || isPending || isRestoring || organizations === null) return if (!session || organizations.length === 0 || status !== "loading") return - if (organizations.length > 1) { + if (isSwitchMode) { setStatus("selection") return } - const onlyOrganization = organizations[0] - if (onlyOrganization) selectOrganization(onlyOrganization) + + const defaultOrganization = + organizations.find((organization) => organization.id === org?.id) ?? + organizations[0] + if (defaultOrganization) { + setSelectedOrgId(defaultOrganization.id) + setStatus("approval") + } }, [ + isSwitchMode, requestError, isPending, isRestoring, + org?.id, organizations, session, status, - selectOrganization, ]) useEffect(() => { @@ -325,9 +324,9 @@ function AuthConnectContent() { measureFades(listRef.current) }, [measureFades, status]) - async function handleConnect() { + async function handleConnect(organization = selectedOrg): Promise { if (requestError || !callback) return - if (!session || !selectedOrg) { + if (!session || !organization) { setError( selectedOrgId ? "That organization is no longer available. Choose another one." @@ -345,7 +344,7 @@ function AuthConnectContent() { const fetchParams = new URLSearchParams({ callback, client: requestedClient, - orgId: selectedOrg.id, + orgId: organization.id, }) const res = await fetch(`${API_URL}/v3/auth/key?${fetchParams}`, { credentials: "include", @@ -362,9 +361,9 @@ function AuthConnectContent() { key: string organization?: { id: string } } - const expectedKeyPrefix = `sm_${selectedOrg.id}_` + const expectedKeyPrefix = `sm_${organization.id}_` if ( - (data.organization && data.organization.id !== selectedOrg.id) || + (data.organization && data.organization.id !== organization.id) || !data.key.startsWith(expectedKeyPrefix) ) { throw new Error( @@ -417,10 +416,22 @@ function AuthConnectContent() { } catch (err) { console.error("Failed to get API key:", err) setError(err instanceof Error ? err.message : "Failed to get API key") - setStatus("approval") + setStatus(isSwitchMode ? "selection" : "approval") } } + function selectOrganization( + organization: NonNullable[number], + ): void { + setError(null) + setSelectedOrgId(organization.id) + if (isSwitchMode) { + void handleConnect(organization) + return + } + setStatus("approval") + } + const isAuthLoading = isPending || isRestoring || organizations === null if (requestError) { @@ -545,6 +556,19 @@ function AuthConnectContent() { ) } + if (isSwitchMode && status === "creating") { + return ( +
+
+
+

+ Switching organization… +

+
+
+ ) + } + if (status === "approval" || status === "creating") { const creating = status === "creating" return ( @@ -584,7 +608,7 @@ function AuthConnectContent() { ))}
-
+
Connecting to @@ -593,20 +617,6 @@ function AuthConnectContent() { {selectedOrg?.name ?? "Organization unavailable"}

- {multiOrg && ( - - )}
{error && ( @@ -645,6 +655,19 @@ function AuthConnectContent() { )}
+ {multiOrg && ( + + )}
From 176f7d61cd4295408ad6e74584a6f357104a694d Mon Sep 17 00:00:00 2001 From: Ishaan Gupta Date: Mon, 24 Aug 2026 19:34:43 +0530 Subject: [PATCH 5/7] restore inline organization change control --- apps/web/app/auth/connect/page.tsx | 31 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/web/app/auth/connect/page.tsx b/apps/web/app/auth/connect/page.tsx index bbd6bb95d..3d1358636 100644 --- a/apps/web/app/auth/connect/page.tsx +++ b/apps/web/app/auth/connect/page.tsx @@ -5,7 +5,7 @@ import { useSession } from "@lib/auth" import { cn } from "@lib/utils" import { Logo } from "@ui/assets/Logo" import { dmSans125ClassName } from "@/lib/fonts" -import { ArrowRight, LoaderIcon, XCircle } from "lucide-react" +import { ArrowLeft, ArrowRight, LoaderIcon, XCircle } from "lucide-react" import Image from "next/image" import { useRouter, useSearchParams } from "next/navigation" import { @@ -608,7 +608,7 @@ function AuthConnectContent() { ))}
-
+
Connecting to @@ -617,6 +617,20 @@ function AuthConnectContent() { {selectedOrg?.name ?? "Organization unavailable"}

+ {multiOrg && ( + + )}
{error && ( @@ -655,19 +669,6 @@ function AuthConnectContent() { )}
- {multiOrg && ( - - )}
From 83e7d8c776c16cb89a4a7d0bb15d13c6a33fcf51 Mon Sep 17 00:00:00 2001 From: Ishaan Gupta Date: Mon, 24 Aug 2026 21:01:46 +0530 Subject: [PATCH 6/7] Show signed-in account on organization selector --- apps/web/app/auth/connect/page.tsx | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/web/app/auth/connect/page.tsx b/apps/web/app/auth/connect/page.tsx index 3d1358636..f69ce0b30 100644 --- a/apps/web/app/auth/connect/page.tsx +++ b/apps/web/app/auth/connect/page.tsx @@ -1,7 +1,7 @@ "use client" import { useAuth } from "@lib/auth-context" -import { useSession } from "@lib/auth" +import { authClient, useSession } from "@lib/auth" import { cn } from "@lib/utils" import { Logo } from "@ui/assets/Logo" import { dmSans125ClassName } from "@/lib/fonts" @@ -324,6 +324,13 @@ function AuthConnectContent() { measureFades(listRef.current) }, [measureFades, status]) + const handleSignOut = useCallback(async () => { + await authClient.signOut().catch(() => undefined) + router.replace( + `/login?redirect=${encodeURIComponent(window.location.href)}`, + ) + }, [router]) + async function handleConnect(organization = selectedOrg): Promise { if (requestError || !callback) return if (!session || !organization) { @@ -551,6 +558,20 @@ function AuthConnectContent() { {error}

)} +
+ {session?.user.email && ( +

+ Signed in as {session.user.email} +

+ )} + +
) From 8f063bd2aeebd34fa58bab1a3fc35bd226485bb4 Mon Sep 17 00:00:00 2001 From: Ishaan Gupta Date: Mon, 24 Aug 2026 21:43:14 +0530 Subject: [PATCH 7/7] Select organization before plugin approval --- apps/web/app/auth/connect/page.tsx | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/apps/web/app/auth/connect/page.tsx b/apps/web/app/auth/connect/page.tsx index f69ce0b30..c3fd18ee5 100644 --- a/apps/web/app/auth/connect/page.tsx +++ b/apps/web/app/auth/connect/page.tsx @@ -193,7 +193,7 @@ function AuthConnectContent() { const params = useSearchParams() const router = useRouter() const { data: session, isPending } = useSession() - const { org, organizations, isRestoring } = useAuth() + const { organizations, isRestoring } = useAuth() const [status, setStatus] = useState("loading") const [error, setError] = useState(null) const [selectedOrgId, setSelectedOrgId] = useState(null) @@ -275,28 +275,8 @@ function AuthConnectContent() { if (requestError || isPending || isRestoring || organizations === null) return if (!session || organizations.length === 0 || status !== "loading") return - if (isSwitchMode) { - setStatus("selection") - return - } - - const defaultOrganization = - organizations.find((organization) => organization.id === org?.id) ?? - organizations[0] - if (defaultOrganization) { - setSelectedOrgId(defaultOrganization.id) - setStatus("approval") - } - }, [ - isSwitchMode, - requestError, - isPending, - isRestoring, - org?.id, - organizations, - session, - status, - ]) + setStatus("selection") + }, [requestError, isPending, isRestoring, organizations, session, status]) useEffect(() => { if (status !== "approval" || !selectedOrgId || organizations === null)